| 情況 | 原因 | 應對 |
| 直接在瀏覽器輸入網址 | 無前一頁 | 導向預設首頁 |
| HTTPS → HTTP 跨協定 | 瀏覽器安全策略不送 Referer | 全站 HTTPS 避免 |
| `Response.Redirect` / `Server.Transfer` 後 | 非使用者點擊導航 | 自行儲存返回網址 |
| `window.location.href` / `location.replace` JS 導向 | 部分瀏覽器不送 Referer | 用 `` 或表單提交 |
| 書籤/收藏夾直接開啟 | 無前一頁 | 導向預設首頁 |
| 防毒/防火牆/隱私外掛過濾 | 移除 Referer 標頭 | 備援機制 |
| Meta Refresh / `` | 不送 Referer | 避免使用 |
健壯返回邏輯實作
1. 登入頁面:儲存 Referer 至 ViewState/Session
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
// 優先取得 Referer
if (Request.UrlReferrer != null)
{
ViewState["ReturnUrl"] = Request.UrlReferrer.ToString();
}
// 次優先:QueryString 指定(如 ?ReturnUrl=/order/detail/123)
else if (!string.IsNullOrEmpty(Request.QueryString["ReturnUrl"]))
{
ViewState["ReturnUrl"] = Request.QueryString["ReturnUrl"];
}
}
}
2. 登入成功後:按優先順序導向
protected void btnLogin_Click(object sender, EventArgs e)
{
if (ValidateUser(txtUser.Text, txtPass.Text))
{
Session["User"] = user;
string returnUrl = null;
// 1. ViewState(同一 Session 內的上一頁)
if (ViewState["ReturnUrl"] != null)
returnUrl = ViewState["ReturnUrl"].ToString();
// 2. Session(跨頁面保持)
else if (Session["ReturnUrl"] != null)
returnUrl = Session["ReturnUrl"].ToString();
// 3. 當前 Request 的 Referer(最後防線)
else if (Request.UrlReferrer != null)
returnUrl = Request.UrlReferrer.ToString();
// 4. 預設首頁
if (string.IsNullOrEmpty(returnUrl) || !IsLocalUrl(returnUrl))
returnUrl = "~/Default.aspx";
Response.Redirect(returnUrl);
}
else
{
ShowError("帳號或密碼錯誤");
}
}
3. 安全驗證:防止開放式重導向攻擊
private bool IsLocalUrl(string url)
{
if (string.IsNullOrEmpty(url)) return false;
Uri absoluteUri;
if (Uri.TryCreate(url, UriKind.Absolute, out absoluteUri))
{
// 必須同域名、同協定、同埠
return absoluteUri.Host == Request.Url.Host
&& absoluteUri.Scheme == Request.Url.Scheme
&& absoluteUri.Port == Request.Url.Port;
}
// 相對路徑視為安全
return url.StartsWith("/") || url.StartsWith("~/");
}
進階:表單 Post 後返回(PRG 模式)
protected void btnSave_Click(object sender, EventArgs e)
{
if (SaveData())
{
// 成功:導向列表頁(或 Referer),避免重新整理重複提交
string returnUrl = ViewState["ReturnUrl"]?.ToString() ?? "~/List.aspx";
Response.Redirect(returnUrl);
}
else
{
// 失敗:停留當前頁顯示錯誤
ShowError("儲存失敗");
}
}
替代方案比較
| 方案 | 優點 | 缺點 | 適用場景 |
| `Request.UrlReferrer` | 零成本、自動 | 不可靠、易被過濾 | 輔助參考 |
| QueryString `ReturnUrl` | 明確、可控、可書籤 | URL 較長 | **推薦主要方式** |
| Session/ViewState 儲存 | 跨請求保持 | Session 佔用記憶體 | 登入流程、多步驟精靈 |
| HTML5 History API (JS) | 前端完全控制 | 需 JS 支援、SPA 適用 | 現代前端架構 |
最佳實務總結
- **主要依賴 `ReturnUrl` 參數** 明確傳遞(連結產生時附加)
- **輔助 `Request.UrlReferrer`** 作為備援
- **Session/ViewState** 保持登入等跨頁面流程
- **必須驗證 `IsLocalUrl`** 防止開放式重導向漏洞
- **提供預設落點**(首頁/儀表板)避免迴圈
建立統一的 `RedirectHelper` 類別封裝上述邏輯,全專案一致呼叫,即可確保返回導向行為可預期、安全、使用者友善。
- 上一篇文章: ASP.NET Repeater 整合 AspNetPager 分頁控制項完整實作
- 下一篇文章: ASP.NET GridView 超連結欄位完整指南:帶參數、新視窗、後端綁定四種實作
Copyright ©
程式碼之家 All Rights Reserved