ASP.NET 中的 URL Rewrite 本質是在 ASP.NET Pipeline 的早期階段,透過 HttpContext.RewritePath() 修改內部請求路徑,使後續處理邏輯 (授權、Handler 選擇、頁面執行) 看到的是「重寫後」的 URL,而瀏覽器地址列保持原樣。
aspnet_isapi.dll 處理HttpContext、HttpRequest、HttpResponseBeginRequest → AuthenticateRequest → AuthorizeRequest →
ResolveRequestCache → AcquireRequestState → PreRequestHandlerExecute →
Handler 執行 (Page/Handler/Service) →
PostRequestHandlerExecute → ReleaseRequestState →
UpdateRequestCache → EndRequest → PreSendRequestHeaders → PreSendRequestContent
原因:BeginRequest 是 Pipeline 第一個事件,此時重寫路徑可影響所有後續事件 (授權判斷、快取鍵值、Handler 映射)。
public class Global : HttpApplication
{
protected void Application_BeginRequest(object sender, EventArgs e)
{
HttpContext context = HttpContext.Current;
string rawPath = context.Request.Path; // 原始路徑 (不含查詢字串)
// 範例 1: 靜態映射 /Customers → /CustomerList.aspx
if (string.Equals(rawPath, "/Customers", StringComparison.OrdinalIgnoreCase))
{
context.RewritePath("~/CustomerList.aspx");
return;
}
// 範例 2: 規則式重寫 /Product/123 → /ProductDetail.aspx?id=123
var match = Regex.Match(rawPath, @"^/Product/(\d+)$", RegexOptions.IgnoreCase);
if (match.Success)
{
string newPath = $"~/ProductDetail.aspx?id={match.Groups[1].Value}";
context.RewritePath(newPath);
return;
}
// 範例 3: 副檔名隱藏 /about → /about.aspx
if (!rawPath.Contains(".") && !rawPath.EndsWith("/"))
{
string aspxPath = rawPath + ".aspx";
if (FileExists(Server.MapPath(aspxPath)))
context.RewritePath(aspxPath);
}
}
private bool FileExists(string physicalPath)
{
return System.IO.File.Exists(physicalPath);
}
}
public void RewritePath(string path, bool rebaseClientPath)
{
// 1. 更新 HttpRequest 內部路徑屬性
_request.Path = path;
_request.FilePath = path;
_request.PathInfo = "";
// 2. 重新計算衍生屬性
_request.PhysicalPath = MapPath(path);
_request.QueryString = ParseQueryString(path); // 若 path 含 ?
// 3. 重新解析 Handler (關鍵!)
// 此步驟在 PreRequestHandlerExecute 事件前自動觸發
}
| 屬性 | 重寫前 | 重寫後 (/CustomerList.aspx) | 說明 |
|---|---|---|---|
| Request.Path | /Customers | /CustomerList.aspx | 內部邏輯見到的路徑 |
| Request.RawUrl | /Customers | /Customers | **不變** - 瀏覽器原始 URL |
| Request.FilePath | /Customers | /CustomerList.aspx | 實體檔案路徑 |
| Request.PathInfo | /Customers | 空字串 | 重置 |
| Request.PhysicalPath | C:\Web\Customers | C:\Web\CustomerList.aspx | 對應實體檔案 |
Global.asax 事件仍適用,但建議改用 IHttpModule 或 Middleware (OWIN/ASP.NET Core)<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="UrlRewriteModule" type="MyApp.UrlRewriteModule" />
</modules>
</system.webServer>
Microsoft.Web.UrlRewrite) 提供 XML 規則引擎,無需寫代碼:
<rewrite>
<rules>
<rule name="ProductDetail" stopProcessing="true">
<match url="^product/([0-9]+)$" />
<action type="Rewrite" url="ProductDetail.aspx?id={R:1}" />
</rule>
</rules>
</rewrite>
| 問題 | 原因 | 解決 |
|---|---|---|
| CSS/JS/圖片 404 | 頁面用相對路徑 css/site.css,重寫後路徑變 /CustomerList.aspx/css/site.css | 使用 根相對路徑 /css/site.css 或 Page.ResolveUrl("~/css/site.css") |
| PostBack 後網址變成重寫後路徑 | <form action 渲染為重寫後路徑 | 在 Page_Load 設定 form1.Action = Request.RawUrl |
| 驗證/授權失效 | <location path="Customers"> 對應原始路徑,但重寫後路徑不同 | 授權規則寫重寫後路徑,或使用 HttpModule 在 AuthorizeRequest 事件自訂邏輯 |
| 無限重寫迴圈 | 規則互相觸發 | 加 stopProcessing="true"、檢查 Request.RawUrl 避免重複重寫 |
ASP.NET Pipeline:每個請求必經的處理管線 (事件序列),屬於 ASP.NET 執行階層。
Web Forms:PageHandlerFactory在 Pipeline 中選擇處理.aspx時,建構頁面生命週期 (Init、Load、Render) 的 頁面框架模型。
Pipeline 選擇 Handler → Handler 可能使用 Web Forms 模型 → 兩者層級不同,Pipeline 更基礎。
Microsoft.AspNetCore.Rewrite Middleware (需 OWIN 橋接) 或官方 URL Rewrite Moduleapp.UseRewriter() + RewriteOptions,支援 Regex、Apache mod_rewrite 規則轉換