「返回上一頁」看似簡單,實則暗藏 Request.UrlReferrer 為 null、回發丟失、AJAX 導航不更新等坑。本文對比 Request.UrlReferrer、ViewState、Session、JS history.back() 四種方案,給出生產環境最佳實踐代碼。
| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| JS history.back() | 無服務端狀態、最快 | 無法跨域、新標籤頁失效、無法攜帶參數 | 純前端導航、模態框關閉 | Request.UrlReferrer | 無需額外存儲 | 易為 null (HTTPS→HTTP、書簽訪問、爬蟲、隱私模式) | 簡單鏈接跳轉、非關鍵業務 | ViewState 保存 UrlReferrer | 頁面級隔離、回發不丟失 | 頁面關閉即失效、ViewState 增大 | 詳情頁返回列表頁 (推薦) | Session 存儲返回地址 | 跨頁面持久化 | 多標籤頁污染、需手動清理 | 多步向導、跨模塊跳轉 |
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
// 關鍵:只在首次加載捕獲,回發時保持 ViewState 原值
if (Request.UrlReferrer != null)
{
ViewState["ReturnUrl"] = Request.UrlReferrer.ToString();
}
else
{
ViewState["ReturnUrl"] = ResolveUrl("~/Default.aspx"); // 兜底首頁
}
}
}
protected void btnReturn_Click(object sender, EventArgs e)
{
string returnUrl = ViewState["ReturnUrl"]?.ToString() ?? ResolveUrl("~/Default.aspx");
Response.Redirect(returnUrl);
}
<asp:HyperLink ID="hlDetail" runat="server"
NavigateUrl='<%# "Detail.aspx?id=" + Eval("Id") + "&returnUrl=" + Server.UrlEncode(Request.RawUrl) %>'>
查看詳情
</asp:HyperLink>
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
string returnUrl = Request.QueryString["returnUrl"];
if (!string.IsNullOrEmpty(returnUrl))
{
ViewState["ReturnUrl"] = returnUrl; // 最高優先級:列表頁傳遞
}
else if (Request.UrlReferrer != null)
{
ViewState["ReturnUrl"] = Request.UrlReferrer.ToString(); // 次優先:瀏覽器Referrer
}
else
{
ViewState["ReturnUrl"] = ResolveUrl("~/Default.aspx"); // 兜底
}
}
}
public class BasePage : System.Web.UI.Page
{
protected string ReturnUrl
{
get => ViewState["ReturnUrl"]?.ToString() ?? ResolveUrl("~/");
set => ViewState["ReturnUrl"] = value;
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
if (!IsPostBack)
{
// 優先級:QueryString > UrlReferrer > Default
ReturnUrl = Request.QueryString["returnUrl"]
?? (Request.UrlReferrer?.ToString())
?? ResolveUrl("~/");
}
}
protected void RedirectBack() => Response.Redirect(ReturnUrl);
}
public static bool IsLocalUrl(this string url, HttpRequest request)
{
if (string.IsNullOrEmpty(url)) return false;
if (url.StartsWith("/")) return true; // 相對路徑
Uri absolute;
return Uri.TryCreate(url, UriKind.Absolute, out absolute)
&& absolute.Host.Equals(request.Url.Host, StringComparison.OrdinalIgnoreCase);
}
// 使用
if (ReturnUrl.IsLocalUrl(Request)) Response.Redirect(ReturnUrl);
else Response.Redirect("~/");
「返回上一頁」核心是可靠捕獲來源地址與跨回發持久化。ViewState + QueryString參數 + UrlReferrer兜底 三層防護,覆蓋 99% 業務場景。封裝至基類 `BasePage`,全項目統一調用 `RedirectBack()`,消除重複代碼與邏輯漏洞。