ASP.NET Core 的 URL 重寫中介軟體
注意
這不是這篇文章的最新版本。 如需目前的版本,請參閱 本文的 .NET 9 版本。
警告
不再支援此版本的 ASP.NET Core。 如需詳細資訊,請參閱 .NET 和 .NET Core 支持原則。 如需目前的版本,請參閱 本文的 .NET 9 版本。
作者:Kirk Larkin 和 Rick Anderson
本文介紹 URL 重寫,並提供如何在 ASP.NET Core 應用程式中使用 URL 重寫中介軟體的指示。
URL 重寫是指根據一或多個預先定義的規則來修改要求 URL 的動作。 URL 重寫會在資源位置和位址之間建立一個抽象層,讓位置和位址不那麼緊密連結。 下列幾種情況非常適合使用 URL 重寫:
- 暫時或永久性移動/取代伺服器資源,並為這些資源保持穩定的定位器時。
- 跨不同應用程式或跨單一應用程式各區域來分割要求處理作業。
- 移除、新增或重組傳入要求的 URL 區段。
- 針對搜尋引擎最佳化 (SEO) 來將公開 URL 最佳化。
- 允許使用易懂的公開 URL,來協助訪客預測要求資源所傳回的內容。
- 將不安全的要求重新導向至安全的端點。
- 防止直接連結,即外部網站透過將另一個網站的資產連結至本身的內容,來盜用該網站擁有的靜態資產。
URL 重寫可能會降低應用程式的效能。 限制規則的數目與複雜度。
URL 重新導向和 URL 重寫
乍看之下,「URL 重新導向」和「URL 重寫」在用語上的差異不太明顯,但卻對提供資源給用戶端方面有重要的影響。 ASP.NET Core 的 URL 重寫中介軟體可以同時滿足這兩種需求。
「URL 重新導向」牽涉有關用戶端的作業,其會指示用戶端到其他位址存取資源,而非用戶端原本要求的位址。 這項作業需要伺服器的來回行程。 當用戶端提出新的資源要求時,系統會將重新導向 URL 傳回給用戶端,並顯示在瀏覽器的網址列中。
若 /resource
重新導向 至 /different-resource
,則伺服器會回應用戶端應前往 /different-resource
取得資源,並提供狀態碼,指出重新導向為暫時性或永久性。
將要求重新導向至不同的 URL 時,可以透過指定具狀態的回應,來指出該重新導向為永久性或暫時性:
如果資源具有新的永久 URL,並且以後對該資源的所有要求都應使用新 URL,則使用
301 - Moved Permanently
狀態碼。 用戶端收到 301 狀態碼時,可能會快取並重複使用回應。如果重新導向為暫時性或很有可能變更時,則使用
302 - Found
狀態碼。 302 狀態碼會指示用戶端不要儲存 URL 並在之後使用。
如需狀態碼的詳細資訊,請參閱 RFC 9110:狀態碼定義。
「URL 重寫」伺服器端作業,會從用戶端要求以外的其他資源位址提供資源。 重寫 URL 並不需要伺服器的來回行程。 系統不會將重寫的 URL 傳回用戶端,也不會顯示在瀏覽器的網址列中。
若 /resource
「重寫」至 /different-resource
,則伺服器會於「內部」擷取資源,並在 /different-resource
傳回資源。
雖然用戶端或許可以在重寫的 URL 處擷取資源,但用戶端並不會在提出要求與接收回應時得知資源位於重寫的 URL 處。
URL 重寫範例應用程式
利用範例應用程式來探索 URL 重寫中介軟體的功能。 範例應用程式會套用重新導向與重寫規則,還會示範多種情況下的重新導向或重寫 URL。
URL 重寫中介軟體的使用時機
當下列方法不能令人滿意時,請使用 URL 重寫中介軟體:
當應用程式裝載於 HTTP.sys 伺服器上時,請使用 URL 重寫中介軟體。
使用 IIS、Apache 和 Nginx 所提供伺服器型 URL 重寫技術的主要原因包括:
中介軟體不支援這些模組的完整功能。
有部分伺服器模組的功能不適用於 ASP.NET Core 專案,例如 IIS Rewrite Module 規則的
IsFile
和IsDirectory
條件約束。 在這些情況下,請改為使用中介軟體。中介軟體的效能可能比不上模組的效能時。
如果要確實得知哪種方法會降低最多效能,或者降低的效能是否可以忽略,進行基準測試是唯一方法。
延伸模組和選項
若要建立 URL 重寫和重新導向規則,您可以使用擴展方法為每個重寫規則建立 RewriteOptions 類別的執行個體。 依照處理順序鏈結多個規則。 系統會使用 UseRewriter 將 RewriteOptions
新增至要求管線,並將其傳入 URL 重寫中介軟體:
using Microsoft.AspNetCore.Rewrite;
using RewriteRules;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
using (StreamReader apacheModRewriteStreamReader =
File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
File.OpenText("IISUrlRewrite.xml"))
{
var options = new RewriteOptions()
.AddRedirect("redirect-rule/(.*)", "redirected/$1")
.AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
skipRemainingRules: true)
.AddApacheModRewrite(apacheModRewriteStreamReader)
.AddIISUrlRewrite(iisUrlRewriteStreamReader)
.Add(MethodRules.RedirectXmlFileRequests)
.Add(MethodRules.RewriteTextFileRequests)
.Add(new RedirectImageRequests(".png", "/png-images"))
.Add(new RedirectImageRequests(".jpg", "/jpg-images"));
app.UseRewriter(options);
}
app.UseStaticFiles();
app.Run(context => context.Response.WriteAsync(
$"Rewritten or Redirected Url: " +
$"{context.Request.Path + context.Request.QueryString}"));
app.Run();
在上述程式碼中,MethodRules
是使用者定義的類別。 如需詳細資訊,請參閱本文中的 RewriteRules.cs
。
將非 www 重新導向 www
有三個選項可讓應用程式將非 www
要求重新導向 www
:
AddRedirectToWwwPermanent:如果要求不是
www
,將要求永久重新導向至www
子網域。 使用 Status308PermanentRedirect 狀態代碼重新導向。AddRedirectToWww:如果傳入要求不是
www
,將要求重新導向至www
子網域。 使用 Status307TemporaryRedirect 狀態代碼重新導向。 多載允許提供回應的狀態碼。 請使用 StatusCodes 類別的欄位來進行狀態碼指派。
URL 重新導向
使用 AddRedirect 將要求重新導向。 第一個參數包含 .NET 規則運算式 (Regex),以比對傳入的 URL 路徑。 第二個參數是取代字串。 第三個參數 (如果有的話) 會指定狀態碼。 如果未指定狀態碼,則狀態碼會預設為 302 - 已找到,表示已暫時移動或取代資源。
using Microsoft.AspNetCore.Rewrite;
using RewriteRules;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
using (StreamReader apacheModRewriteStreamReader =
File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
File.OpenText("IISUrlRewrite.xml"))
{
var options = new RewriteOptions()
.AddRedirect("redirect-rule/(.*)", "redirected/$1")
.AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
skipRemainingRules: true)
.AddApacheModRewrite(apacheModRewriteStreamReader)
.AddIISUrlRewrite(iisUrlRewriteStreamReader)
.Add(MethodRules.RedirectXmlFileRequests)
.Add(MethodRules.RewriteTextFileRequests)
.Add(new RedirectImageRequests(".png", "/png-images"))
.Add(new RedirectImageRequests(".jpg", "/jpg-images"));
app.UseRewriter(options);
}
app.UseStaticFiles();
app.Run(context => context.Response.WriteAsync(
$"Rewritten or Redirected Url: " +
$"{context.Request.Path + context.Request.QueryString}"));
app.Run();
在啟用開發人員工具的瀏覽器中,使用 /redirect-rule/1234/5678
路徑提出範例應用程式的要求。 規則運算式會比對 redirect-rule/(.*)
上的要求路徑,並會將路徑取代為 /redirected/1234/5678
。 系統會將重新導向 URL 與 302 (已找到) 狀態碼傳回給用戶端。 瀏覽器在重新導向 URL 處提出新要求,該 URL 也會顯示在瀏覽器的網址列中。 因為範例應用程式與重新導向 URL 沒有任何相符規則:
- 第二個要求會從應用程式收到 200 (確定) 回應。
- 回應的本文會顯示重新導向 URL。
「重新導向」URL 時,會對伺服器進行來回行程。
警告
建立重新導向規則時,請務必謹慎。 每向應用程式提出要求時 (包括重新導向後),請評估您的重新導向規則。 因為您有可能會不小心建立無限重新導向迴圈。
括弧內所含的運算式部分稱為「擷取群組」。 運算式的點 (.
) 表示「比對任何字元」。 星號 (*
) 表示「比對前置字元零或多次」。 因此,擷取群組 (.*)
會擷取 URL 的 1234/5678
最後這兩個路徑區段。 在要求 URL 中於 redirect-rule/
之後提供的任何值都會被這個單一擷取群組擷取。
系統會將擷取的群組以貨幣符號 ($
) 後接擷取序號的形式,插入取代字串中。 第一個擷取群組值是用 $1
取得,第二個是用 $2
取得,然後按規則運算式 (RegEx) 的擷取群組順序以此類推。 redirect-rule/(.*)
的重新導向規則運算式中只有一個擷取的群組,因此取代字串中只有一個插入群組,即 $1
。 套用規則時,URL 會變成 /redirected/1234/5678
。
嘗試使用 [網路] 索引標籤上的瀏覽器工具來執行 /redirect-rule/1234/5678
。
將 URL 重新導向至安全端點
使用 AddRedirectToHttps 將 HTTP 要求重新導向至使用 HTTPS 通訊協定的相同主機與路徑。 如果未提供狀態碼,中介軟體會預設為 302 (已找到)。 若未提供連接埠:
- 中介軟體會預設為
null
。 - 配置會變更為
https
(HTTPS 通訊協定),且用戶端會在連接埠 443 上存取資源。
下列範例示範如何將狀態碼設定為 301 - Moved Permanently
,並將連接埠變更為 localhost 上 Kestrel 所使用的 HTTPS 連接埠。 在生產環境中,HTTPS 連接埠會設定為 null:
using Microsoft.AspNetCore.Rewrite;
using RewriteRules;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
int? localhostHTTPSport = null;
if (app.Environment.IsDevelopment())
{
localhostHTTPSport = Int32.Parse(Environment.GetEnvironmentVariable(
"ASPNETCORE_URLS")!.Split(new Char[] { ':', ';' })[2]);
}
using (StreamReader apacheModRewriteStreamReader =
File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
File.OpenText("IISUrlRewrite.xml"))
{
var options = new RewriteOptions()
// localhostHTTPport not needed for production, used only with localhost.
.AddRedirectToHttps(301, localhostHTTPSport)
.AddRedirect("redirect-rule/(.*)", "redirected/$1")
.AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
skipRemainingRules: true)
.AddApacheModRewrite(apacheModRewriteStreamReader)
.AddIISUrlRewrite(iisUrlRewriteStreamReader)
.Add(MethodRules.RedirectXmlFileRequests)
.Add(MethodRules.RewriteTextFileRequests)
.Add(new RedirectImageRequests(".png", "/png-images"))
.Add(new RedirectImageRequests(".jpg", "/jpg-images"));
app.UseRewriter(options);
}
app.UseStaticFiles();
app.Run(context => context.Response.WriteAsync(
$"Rewritten or Redirected Url: " +
$"{context.Request.Path + context.Request.QueryString}"));
app.Run();
使用 AddRedirectToHttpsPermanent 將不安全的要求重新導向至使用安全 HTTPS 通訊協定 (在連接埠 443 上) 的相同主機與路徑。 中介軟體會將狀態碼設定為 301 - Moved Permanently
。
using Microsoft.AspNetCore.Rewrite;
using RewriteRules;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
using (StreamReader apacheModRewriteStreamReader =
File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
File.OpenText("IISUrlRewrite.xml"))
{
var options = new RewriteOptions()
.AddRedirectToHttpsPermanent()
.AddRedirect("redirect-rule/(.*)", "redirected/$1")
.AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
skipRemainingRules: true)
.AddApacheModRewrite(apacheModRewriteStreamReader)
.AddIISUrlRewrite(iisUrlRewriteStreamReader)
.Add(MethodRules.RedirectXmlFileRequests)
.Add(MethodRules.RewriteTextFileRequests)
.Add(new RedirectImageRequests(".png", "/png-images"))
.Add(new RedirectImageRequests(".jpg", "/jpg-images"));
app.UseRewriter(options);
}
app.UseStaticFiles();
app.Run(context => context.Response.WriteAsync(
$"Rewritten or Redirected Url: " +
$"{context.Request.Path + context.Request.QueryString}"));
app.Run();
注意
在不要求額外重新導向規則的情況下重新導向至安全的端點時,建議使用 HTTPS 重新導向中介軟體。 如需詳細資訊,請參閱強制執行 HTTPS。
範例應用程式示範如何使用 AddRedirectToHttps
或 AddRedirectToHttpsPermanent
。 在 http://redirect6.azurewebsites.net/iis-rules-rewrite/xyz
向對應用程式提出不安全的 HTTP 要求。 使用 localhost 測試 HTTP 至 HTTPS 重新導向時:
- 使用 HTTP URL,其連接埠與 HTTPS URL 不同。 HTTP URL 位於
Properties/launchSettings.json
檔案中。 - 從
https://localhost/{port}
中移除s
失敗,因為 localhost 不會透過 HTTP 來回應 HTTPS 連接埠。
下圖顯示使用上述程式碼向 http://redirect6.azurewebsites.net/iis-rules-rewrite/xyz
發出要求的 F12 瀏覽器工具圖像:
URL 重寫
使用 AddRewrite 來建立 URL 重寫的規則。 第一個參數中包含規則運算式,用於比對傳入的 URL 路徑。 第二個參數是取代字串。 第三個參數 skipRemainingRules: {true|false}
指出中介軟體是否要在套用目前規則時略過其他重寫規則。
using Microsoft.AspNetCore.Rewrite;
using RewriteRules;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
using (StreamReader apacheModRewriteStreamReader =
File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
File.OpenText("IISUrlRewrite.xml"))
{
var options = new RewriteOptions()
.AddRedirectToHttpsPermanent()
.AddRedirect("redirect-rule/(.*)", "redirected/$1")
.AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
skipRemainingRules: true)
.AddApacheModRewrite(apacheModRewriteStreamReader)
.AddIISUrlRewrite(iisUrlRewriteStreamReader)
.Add(MethodRules.RedirectXmlFileRequests)
.Add(MethodRules.RewriteTextFileRequests)
.Add(new RedirectImageRequests(".png", "/png-images"))
.Add(new RedirectImageRequests(".jpg", "/jpg-images"));
app.UseRewriter(options);
}
app.UseStaticFiles();
app.Run(context => context.Response.WriteAsync(
$"Rewritten or Redirected Url: " +
$"{context.Request.Path + context.Request.QueryString}"));
app.Run();
嘗試向 https://redirect6.azurewebsites.net/rewrite-rule/1234/5678
發出要求
運算式開頭的插入號 (^
) 表示,比對會從 URL 路徑的開頭處開始。
在先前使用重新導向規則 redirect-rule/(.*)
的範例中,規則運算式的開頭並沒有插入號 (^
)。 因此,就算 redirect-rule/
前有任何字元也能成功比對。
路徑 | 比對 |
---|---|
/redirect-rule/1234/5678 |
Yes |
/my-cool-redirect-rule/1234/5678 |
.是 |
/anotherredirect-rule/1234/5678 |
Yes |
^rewrite-rule/(\d+)/(\d+)
重寫規則只會比對開頭為 rewrite-rule/
的路徑。 請注意下表中的比對差異。
路徑 | 比對 |
---|---|
/rewrite-rule/1234/5678 |
是 |
/my-cool-rewrite-rule/1234/5678 |
無 |
/anotherrewrite-rule/1234/5678 |
No |
運算式的 ^rewrite-rule/
部分之後,有 (\d+)/(\d+)
這兩個擷取群組。 \d
表示「比對數字」。 加號 (+
) 表示「比對一或多個前置字元」。 因此,URL 必須包含某個數字,後接斜線與另一個數字。 這些擷取群組會以 $1
和 $2
形式插入重寫的 URL。 重寫規則的取代字串會將擷取的群組放入查詢字串中。 要求的路徑 /rewrite-rule/1234/5678
會被重寫以傳回位於 /rewritten?var1=1234&var2=5678
的資源。 如果原始要求上有查詢字串,則會在重寫 URL 時予以保留。
這麼做就不需要伺服器的來回行程,即可傳回資源。 如果資源存在,就會擷取該資源,並傳回 200 (確定) 狀態碼給用戶端。 因為系統並未重新導向用戶端,因此瀏覽器網址列中的 URL 不會變更。 用戶端也無法偵測到伺服器上發生 URL 重寫作業。
URL 重寫和重新導向的效能秘訣
為獲得最快的回應:
- 排序重寫規則;從最常比對的規則排到最不常比對的規則。
- 因為比對規則是計算繁複的程序,且會增加應用程式的回應時間,所以請盡可能使用
skipRemainingRules: true
。 當出現符合項目且不需要任何額外的規則處理時,即略過剩下的規則處理。
警告
惡意使用者可以提供一些處理成本極高的輸入給 RegularExpressions
,從而導致拒絕服務攻擊。 使用 RegularExpressions
的 ASP.NET Core 架構 API 會傳遞逾時。 例如,RedirectRule 和 RewriteRule 類別都會傳入一秒的逾時。
Apache mod_rewrite
使用 AddApacheModRewrite 來套用 Apache mod_rewrite 規則。 請確認應用程式已部署規則檔。 如需 mod_rewrite 規則的詳細資訊和範例,請參閱 Apache mod_rewrite。
系統會使用 StreamReader 來讀取 ApacheModRewrite.txt 規則檔案的規則:
using Microsoft.AspNetCore.Rewrite;
using RewriteRules;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
using (StreamReader apacheModRewriteStreamReader =
File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
File.OpenText("IISUrlRewrite.xml"))
{
var options = new RewriteOptions()
.AddRedirectToHttpsPermanent()
.AddRedirect("redirect-rule/(.*)", "redirected/$1")
.AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
skipRemainingRules: true)
.AddApacheModRewrite(apacheModRewriteStreamReader)
.AddIISUrlRewrite(iisUrlRewriteStreamReader)
.Add(MethodRules.RedirectXmlFileRequests)
.Add(MethodRules.RewriteTextFileRequests)
.Add(new RedirectImageRequests(".png", "/png-images"))
.Add(new RedirectImageRequests(".jpg", "/jpg-images"));
app.UseRewriter(options);
}
app.UseStaticFiles();
app.Run(context => context.Response.WriteAsync(
$"Rewritten or Redirected Url: " +
$"{context.Request.Path + context.Request.QueryString}"));
app.Run();
範例應用程式會將要求從 /apache-mod-rules-redirect/(.\*)
重新導向至 /redirected?id=$1
。 回應狀態碼為 302 (已找到)。
# Rewrite path with additional sub directory
RewriteRule ^/apache-mod-rules-redirect/(.*) /redirected?id=$1 [L,R=302]
嘗試向 https://redirect6.azurewebsites.net/apache-mod-rules-redirect/1234
發出要求
Apache 中介軟體支援下列 Apache mod_rewrite 伺服器變數:
- CONN_REMOTE_ADDR
- HTTP_ACCEPT
- HTTP_CONNECTION
- HTTP_COOKIE
- HTTP_FORWARDED
- HTTP_HOST
- HTTP_REFERER
- HTTP_USER_AGENT
- HTTPS
- IPV6
- QUERY_STRING
- REMOTE_ADDR
- REMOTE_PORT
- REQUEST_FILENAME
- REQUEST_METHOD
- REQUEST_SCHEME
- REQUEST_URI
- SCRIPT_FILENAME
- SERVER_ADDR
- SERVER_PORT
- SERVER_PROTOCOL
- TIME
- TIME_DAY
- TIME_HOUR
- TIME_MIN
- TIME_MON
- TIME_SEC
- TIME_WDAY
- TIME_YEAR
IIS URL Rewrite Module 規則
若要使用與套用至 IIS URL Rewrite Module 一樣的規則集,請使用 AddIISUrlRewrite。 請確認應用程式已部署規則檔。 在 Windows Server IIS 上執行時,請不要引導中介軟體使用應用程式的 web.config 檔案。 使用 IIS 時,這些規則應該儲存在應用程式的 web.config 檔案外部,以免與 IIS Rewrite Module 發生衝突。 如需 IIS URL Rewrite Module 規則的詳細資訊和範例,請參閱 Using Url Rewrite Module 2.0 (使用 URL Rewrite Module 2.0) 和 URL Rewrite Module Configuration Reference (URL Rewrite Module 組態參考)。
使用 StreamReader 從 IISUrlRewrite.xml
規則檔案讀取規則:
using Microsoft.AspNetCore.Rewrite;
using RewriteRules;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
using (StreamReader apacheModRewriteStreamReader =
File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
File.OpenText("IISUrlRewrite.xml"))
{
var options = new RewriteOptions()
.AddRedirectToHttpsPermanent()
.AddRedirect("redirect-rule/(.*)", "redirected/$1")
.AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
skipRemainingRules: true)
.AddApacheModRewrite(apacheModRewriteStreamReader)
.AddIISUrlRewrite(iisUrlRewriteStreamReader)
.Add(MethodRules.RedirectXmlFileRequests)
.Add(MethodRules.RewriteTextFileRequests)
.Add(new RedirectImageRequests(".png", "/png-images"))
.Add(new RedirectImageRequests(".jpg", "/jpg-images"));
app.UseRewriter(options);
}
app.UseStaticFiles();
app.Run(context => context.Response.WriteAsync(
$"Rewritten or Redirected Url: " +
$"{context.Request.Path + context.Request.QueryString}"));
app.Run();
範例應用程式會將要求從 /iis-rules-rewrite/(.*)
重寫至 /rewritten?id=$1
。 系統會將回應與 200 (確定) 狀態碼傳送給用戶端。
<rewrite>
<rules>
<rule name="Rewrite segment to id querystring" stopProcessing="true">
<match url="^iis-rules-rewrite/(.*)$" />
<action type="Rewrite" url="rewritten?id={R:1}" appendQueryString="false"/>
</rule>
</rules>
</rewrite>
嘗試向 https://redirect6.azurewebsites.net/iis-rules-rewrite/xyz
發出要求
具有作用中 IIS 重寫模組的應用程式,且其已設定伺服器層級規則,以不當方式影響應用程式:
- 請考慮停用應用程式的 IIS 重寫模組。
- 如需詳細資訊,請參閱停用 IIS 模組。
不支援的功能
中介軟體不支援下列 IIS URL 重寫模組功能:
- 輸出規則
- 自訂伺服器變數
- 萬用字元
- LogRewrittenUrl
支援的伺服器變數
中介軟體支援下列 IIS URL 重寫模組伺服器變數:
- CONTENT_LENGTH
- CONTENT_TYPE
- HTTP_ACCEPT
- HTTP_CONNECTION
- HTTP_COOKIE
- HTTP_HOST
- HTTP_REFERER
- HTTP_URL
- HTTP_USER_AGENT
- HTTPS
- LOCAL_ADDR
- QUERY_STRING
- REMOTE_ADDR
- REMOTE_PORT
- REQUEST_FILENAME
- REQUEST_URI
可以透過 PhysicalFileProvider 取得 IFileProvider。 這種方法可讓重寫規則檔案的位置更有彈性。 請確認重寫規則檔案已部署到位於所提供路徑的伺服器。
var fileProvider = new PhysicalFileProvider(Directory.GetCurrentDirectory());
以方法為基礎的規則
使用 Add 在方法中實作自訂規則邏輯。 Add
會公開 RewriteContext,使 HttpContext 可用於重新導向方法。 RewriteContext.Result 屬性可判斷其他管線處理的執行方式。 請將值設定為下表中描述的其中一個 RuleResult 欄位。
重寫內容結果 | 動作 |
---|---|
RuleResult.ContinueRules (預設值) |
繼續套用規則。 |
RuleResult.EndResponse |
停止套用規則,並傳送回應。 |
RuleResult.SkipRemainingRules |
停止套用規則,並將內容傳送至下一個中介軟體。 |
using Microsoft.AspNetCore.Rewrite;
using RewriteRules;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
using (StreamReader apacheModRewriteStreamReader =
File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
File.OpenText("IISUrlRewrite.xml"))
{
var options = new RewriteOptions()
.AddRedirectToHttpsPermanent()
.AddRedirect("redirect-rule/(.*)", "redirected/$1")
.AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
skipRemainingRules: true)
.AddApacheModRewrite(apacheModRewriteStreamReader)
.AddIISUrlRewrite(iisUrlRewriteStreamReader)
.Add(MethodRules.RedirectXmlFileRequests)
.Add(MethodRules.RewriteTextFileRequests)
.Add(new RedirectImageRequests(".png", "/png-images"))
.Add(new RedirectImageRequests(".jpg", "/jpg-images"));
app.UseRewriter(options);
}
app.UseStaticFiles();
app.Run(context => context.Response.WriteAsync(
$"Rewritten or Redirected Url: " +
$"{context.Request.Path + context.Request.QueryString}"));
app.Run();
範例應用程式示範了一種方法,可將以 .xml
結尾之路徑的要求重新導向到其他位置。 對 /file.xml
提出要求時:
- 會將要求重新導向至
/xmlfiles/file.xml
- 狀態碼則設定為
301 - Moved Permanently
。 當瀏覽器對/xmlfiles/file.xml
提出新的要求時,靜態檔案中介軟體會從 wwwroot/xmlfiles 資料夾將檔案提供給用戶端。 若要重新導向,請明確設定回應的狀態碼。 否則會傳回 200 (確定) 狀態碼,用戶端上也不會發生重新導向。
RewriteRules.cs
:
public static void RedirectXmlFileRequests(RewriteContext context)
{
var request = context.HttpContext.Request;
// Because the client is redirecting back to the same app, stop
// processing if the request has already been redirected.
if (request.Path.StartsWithSegments(new PathString("/xmlfiles")) ||
request.Path.Value==null)
{
return;
}
if (request.Path.Value.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
{
var response = context.HttpContext.Response;
response.StatusCode = (int) HttpStatusCode.MovedPermanently;
context.Result = RuleResult.EndResponse;
response.Headers[HeaderNames.Location] =
"/xmlfiles" + request.Path + request.QueryString;
}
}
這種方法也能重寫要求。 範例應用程式示範了如何重寫任何文字檔要求的路徑,以從 wwwroot 資料夾提供 file.txt 文字檔。 靜態檔案中介軟體會根據更新的要求路徑來提供檔案:
using Microsoft.AspNetCore.Rewrite;
using RewriteRules;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
using (StreamReader apacheModRewriteStreamReader =
File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
File.OpenText("IISUrlRewrite.xml"))
{
var options = new RewriteOptions()
.AddRedirectToHttpsPermanent()
.AddRedirect("redirect-rule/(.*)", "redirected/$1")
.AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
skipRemainingRules: true)
.AddApacheModRewrite(apacheModRewriteStreamReader)
.AddIISUrlRewrite(iisUrlRewriteStreamReader)
.Add(MethodRules.RedirectXmlFileRequests)
.Add(MethodRules.RewriteTextFileRequests)
.Add(new RedirectImageRequests(".png", "/png-images"))
.Add(new RedirectImageRequests(".jpg", "/jpg-images"));
app.UseRewriter(options);
}
app.UseStaticFiles();
app.Run(context => context.Response.WriteAsync(
$"Rewritten or Redirected Url: " +
$"{context.Request.Path + context.Request.QueryString}"));
app.Run();
RewriteRules.cs
:
public static void RewriteTextFileRequests(RewriteContext context)
{
var request = context.HttpContext.Request;
if (request.Path.Value != null &&
request.Path.Value.EndsWith(".txt", StringComparison.OrdinalIgnoreCase))
{
context.Result = RuleResult.SkipRemainingRules;
request.Path = "/file.txt";
}
}
以 IRule 為基礎的規則
利用 Add 來使用類別中實作 IRule 介面的專屬規則邏輯。 相較於使用以方法為基礎的規則方法,IRule
提供了更高彈性。 實作類別可以包含建構函式,以便在其中傳入 ApplyRule 方法的參數。
using Microsoft.AspNetCore.Rewrite;
using RewriteRules;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
using (StreamReader apacheModRewriteStreamReader =
File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
File.OpenText("IISUrlRewrite.xml"))
{
var options = new RewriteOptions()
.AddRedirectToHttpsPermanent()
.AddRedirect("redirect-rule/(.*)", "redirected/$1")
.AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
skipRemainingRules: true)
.AddApacheModRewrite(apacheModRewriteStreamReader)
.AddIISUrlRewrite(iisUrlRewriteStreamReader)
.Add(MethodRules.RedirectXmlFileRequests)
.Add(MethodRules.RewriteTextFileRequests)
.Add(new RedirectImageRequests(".png", "/png-images"))
.Add(new RedirectImageRequests(".jpg", "/jpg-images"));
app.UseRewriter(options);
}
app.UseStaticFiles();
app.Run(context => context.Response.WriteAsync(
$"Rewritten or Redirected Url: " +
$"{context.Request.Path + context.Request.QueryString}"));
app.Run();
為了滿足若干條件,系統會檢查範例應用程式中的 extension
和 newPath
參數值。 extension
必須包含一值,而且此值必須是 .png
、.jpg
或 .gif
。 如果 newPath
無效,就會擲回 ArgumentException。 若要求是針對 image.png
發出,則要求會重新導向至 /png-images/image.png
。 若要求是針對 image.jpg
發出,則要求會重新導向至 /jpg-images/image.jpg
。 狀態碼會設定為 301 - Moved Permanently
,而 context.Result
會設定為停止處理規則並傳送回應。
public class RedirectImageRequests : IRule
{
private readonly string _extension;
private readonly PathString _newPath;
public RedirectImageRequests(string extension, string newPath)
{
if (string.IsNullOrEmpty(extension))
{
throw new ArgumentException(nameof(extension));
}
if (!Regex.IsMatch(extension, @"^\.(png|jpg|gif)$"))
{
throw new ArgumentException("Invalid extension", nameof(extension));
}
if (!Regex.IsMatch(newPath, @"(/[A-Za-z0-9]+)+?"))
{
throw new ArgumentException("Invalid path", nameof(newPath));
}
_extension = extension;
_newPath = new PathString(newPath);
}
public void ApplyRule(RewriteContext context)
{
var request = context.HttpContext.Request;
// Because we're redirecting back to the same app, stop
// processing if the request has already been redirected
if (request.Path.StartsWithSegments(new PathString(_newPath)) ||
request.Path.Value == null)
{
return;
}
if (request.Path.Value.EndsWith(_extension, StringComparison.OrdinalIgnoreCase))
{
var response = context.HttpContext.Response;
response.StatusCode = (int) HttpStatusCode.MovedPermanently;
context.Result = RuleResult.EndResponse;
response.Headers[HeaderNames.Location] =
_newPath + request.Path + request.QueryString;
}
}
}
請嘗試︰
- PNG 要求:
https://redirect6.azurewebsites.net/image.png
- JPG 要求:
https://redirect6.azurewebsites.net/image.jpg
Regex 範例
目標 | Regex 字串及 比對範例 |
取代字串及 輸出範例 |
---|---|---|
將路徑重寫成查詢字串 | ^path/(.*)/(.*) /path/abc/123 |
path?var1=$1&var2=$2 /path?var1=abc&var2=123 |
移除斜線 | ^path2/(.*)/$ /path2/xyz/ |
$1 /path2/xyz |
強制使用斜線 | ^path3/(.*[^/])$ /path3/xyz |
$1/ /path3/xyz/ |
避免重寫特定的要求 | ^(.*)(?<!\.axd)$ 或 ^(?!.*\.axd$)(.*)$ 是: /path4/resource.htm 否: /path4/resource.axd |
rewritten/$1 /rewritten/resource.htm /resource.axd |
重新排列 URL 區段 | path5/(.*)/(.*)/(.*) path5/1/2/3 |
path5/$3/$2/$1 path5/3/2/1 |
取代 URL 區段 | ^path6/(.*)/segment2/(.*) ^path6/segment1/segment2/segment3 |
path6/$1/replaced/$2 /path6/segment1/replaced/segment3 |
上表中的連結會使用部署至 Azure 的下列程式碼:
using Microsoft.AspNetCore.Rewrite;
using RewriteRules;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
using (StreamReader apacheModRewriteStreamReader =
File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
File.OpenText("IISUrlRewrite.xml"))
{
var options = new RewriteOptions()
.AddRedirectToHttpsPermanent()
.AddRedirect("redirect-rule/(.*)", "redirected/$1")
.AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
skipRemainingRules: true)
// Rewrite path to QS.
.AddRewrite(@"^path/(.*)/(.*)", "path?var1=$1&var2=$2",
skipRemainingRules: true)
// Skip trailing slash.
.AddRewrite(@"^path2/(.*)/$", "path2/$1",
skipRemainingRules: true)
// Enforce trailing slash.
.AddRewrite(@"^path3/(.*[^/])$", "path3/$1/",
skipRemainingRules: true)
// Avoid rewriting specific requests.
.AddRewrite(@"^path4/(.*)(?<!\.axd)$", "rewritten/$1",
skipRemainingRules: true)
// Rearrange URL segments
.AddRewrite(@"^path5/(.*)/(.*)/(.*)", "path5/$3/$2/$1",
skipRemainingRules: true)
// Replace a URL segment
.AddRewrite(@"^path6/(.*)/segment2/(.*)", "path6/$1/replaced/$2",
skipRemainingRules: true)
.AddApacheModRewrite(apacheModRewriteStreamReader)
.AddIISUrlRewrite(iisUrlRewriteStreamReader)
.Add(MethodRules.RedirectXmlFileRequests)
.Add(MethodRules.RewriteTextFileRequests)
.Add(new RedirectImageRequests(".png", "/png-images"))
.Add(new RedirectImageRequests(".jpg", "/jpg-images"));
app.UseRewriter(options);
}
app.UseStaticFiles();
app.Run(context => context.Response.WriteAsync(
$"Rewritten or Redirected Url: " +
$"{context.Request.Path + context.Request.QueryString}"));
app.Run();
上述大部分的規則運算式範例中,常值 path
是用來為已部署的範例建立唯一可測試的重寫規則。 一般而言,規則運算式不會包含 path
。 例如,請參閱這些規則運算式範例資料表。
本文件介紹 URL 重寫,並提供如何在 ASP.NET Core 應用程式中使用 URL 重寫中介軟體的指示。
URL 重寫是指根據一或多個預先定義的規則來修改要求 URL 的動作。 URL 重寫會在資源位置和位址之間建立一個抽象層,讓位置和位址不那麼緊密連結。 下列幾種情況非常適合使用 URL 重寫:
- 暫時或永久性移動/取代伺服器資源,並為這些資源保持穩定的定位器時。
- 跨不同應用程式或跨單一應用程式各區域來分割要求處理作業。
- 移除、新增或重組傳入要求的 URL 區段。
- 針對搜尋引擎最佳化 (SEO) 來將公開 URL 最佳化。
- 允許使用易懂的公開 URL,來協助訪客預測要求資源所傳回的內容。
- 將不安全的要求重新導向至安全的端點。
- 防止直接連結,即外部網站透過將另一個網站的資產連結至本身的內容,來盜用該網站擁有的靜態資產。
注意
URL 重寫可能會降低應用程式的效能。 如果可行的話,請限制規則的數目與複雜程度。
檢視或下載範例程式碼 \(英文\) (如何下載)
URL 重新導向和 URL 重寫
乍看之下,「URL 重新導向」和「URL 重寫」在用語上的差異不太明顯,但卻對提供資源給用戶端方面有重要的影響。 ASP.NET Core 的 URL 重寫中介軟體可以同時滿足這兩種需求。
「URL 重新導向」牽涉有關用戶端的作業,其會指示用戶端到其他位址存取資源,而非用戶端原本要求的位址。 這項作業需要伺服器的來回行程。 當用戶端提出新的資源要求時,系統會將重新導向 URL 傳回給用戶端,並顯示在瀏覽器的網址列中。
若 /resource
重新導向 至 /different-resource
,則伺服器會回應用戶端應前往 /different-resource
取得資源,並提供狀態碼,指出重新導向為暫時性或永久性。
將要求重新導向至不同的 URL 時,可以透過指定具狀態的回應,來指出該重新導向為永久性或暫時性:
如果資源具有新的永久 URL,且您希望指示用戶端以後對該資源的所有請求都應使用新 URL,請使用
301 - Moved Permanently
狀態碼。 用戶端收到 301 狀態碼時,可能會快取並重複使用回應。若重新導向為暫時性或很有可能變更時,請使用 302 (已找到) 狀態碼。 302 狀態碼會指示用戶端不要儲存 URL 並在之後使用。
如需狀態碼的詳細資訊,請參閱 RFC 9110:狀態碼定義。
「URL 重寫」伺服器端作業,會從用戶端要求以外的其他資源位址提供資源。 重寫 URL 並不需要伺服器的來回行程。 系統不會將重寫的 URL 傳回用戶端,也不會顯示在瀏覽器的網址列中。
若 /resource
「重寫」至 /different-resource
,則伺服器會於「內部」擷取資源,並在 /different-resource
傳回資源。
雖然用戶端或許可以在重寫的 URL 處擷取資源,但用戶端並不會在提出要求與接收回應時得知資源位於重寫的 URL 處。
URL 重寫範例應用程式
您可以利用範例應用程式,來探索 URL 重寫中介軟體的功能。 範例應用程式會套用重新導向與重寫規則,還會示範多種情況下的重新導向或重寫 URL。
URL 重寫中介軟體的使用時機
當您無法使用以下方法時,請使用重寫中介軟體:
當應用程式裝載於 HTTP.sys 伺服器上時,請使用 URL 重寫中介軟體。
使用 IIS、Apache 和 Nginx 所提供伺服器型 URL 重寫技術的主要原因包括:
中介軟體不支援這些模組的完整功能。
有部分伺服器模組的功能不適用於 ASP.NET Core 專案,例如 IIS Rewrite Module 規則的
IsFile
和IsDirectory
條件約束。 在這些情況下,請改為使用中介軟體。中介軟體的效能可能比不上模組的效能時。
如果要確實得知哪種方法會降低最多效能,或是降低的效能可以忽略的話,進行效能評定是唯一方法。
套件
URL 重寫中介軟體由 Microsoft.AspNetCore.Rewrite 套件所提供,其會以隱含方式包含在 ASP.NET Core 應用程式中。
延伸模組和選項
若要建立 URL 重寫和重新導向規則,您可以針對每個重寫規則的擴充方法建立 RewriteOptions 類別的執行個體。 依據所需的處理順序來鏈結多個規則。 系統會使用 UseRewriter 將 RewriteOptions
新增至要求管線,並將其傳入 URL 重寫中介軟體:
public void Configure(IApplicationBuilder app)
{
using (StreamReader apacheModRewriteStreamReader =
File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
File.OpenText("IISUrlRewrite.xml"))
{
var options = new RewriteOptions()
.AddRedirect("redirect-rule/(.*)", "redirected/$1")
.AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
skipRemainingRules: true)
.AddApacheModRewrite(apacheModRewriteStreamReader)
.AddIISUrlRewrite(iisUrlRewriteStreamReader)
.Add(MethodRules.RedirectXmlFileRequests)
.Add(MethodRules.RewriteTextFileRequests)
.Add(new RedirectImageRequests(".png", "/png-images"))
.Add(new RedirectImageRequests(".jpg", "/jpg-images"));
app.UseRewriter(options);
}
app.UseStaticFiles();
app.Run(context => context.Response.WriteAsync(
$"Rewritten or Redirected Url: " +
$"{context.Request.Path + context.Request.QueryString}"));
}
將非 www 重新導向 www
有三個選項可讓應用程式將非 www
要求重新導向 www
:
AddRedirectToWwwPermanent:如果要求不是
www
,將要求永久重新導向至www
子網域。 使用 Status308PermanentRedirect 狀態代碼重新導向。AddRedirectToWww:如果傳入要求不是
www
,將要求重新導向至www
子網域。 使用 Status307TemporaryRedirect 狀態代碼重新導向。 多載可讓您提供回應的狀態代碼。 請使用 StatusCodes 類別的欄位來進行狀態碼指派。
URL 重新導向
使用 AddRedirect 將要求重新導向。 第一個參數會包含您的 Regex,以比對傳入 URL 的路徑。 第二個參數是取代字串。 第三個參數 (如果有的話) 會指定狀態碼。 如果您未指定狀態碼,則狀態碼會預設為 302 (已找到),表示已暫時移動或取代資源。
public void Configure(IApplicationBuilder app)
{
using (StreamReader apacheModRewriteStreamReader =
File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
File.OpenText("IISUrlRewrite.xml"))
{
var options = new RewriteOptions()
.AddRedirect("redirect-rule/(.*)", "redirected/$1")
.AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
skipRemainingRules: true)
.AddApacheModRewrite(apacheModRewriteStreamReader)
.AddIISUrlRewrite(iisUrlRewriteStreamReader)
.Add(MethodRules.RedirectXmlFileRequests)
.Add(MethodRules.RewriteTextFileRequests)
.Add(new RedirectImageRequests(".png", "/png-images"))
.Add(new RedirectImageRequests(".jpg", "/jpg-images"));
app.UseRewriter(options);
}
app.UseStaticFiles();
app.Run(context => context.Response.WriteAsync(
$"Rewritten or Redirected Url: " +
$"{context.Request.Path + context.Request.QueryString}"));
}
在啟用開發人員工具的瀏覽器中,使用 /redirect-rule/1234/5678
路徑提出範例應用程式的要求。 Regex 會比對 redirect-rule/(.*)
上的要求路徑,並會將路徑取代為 /redirected/1234/5678
。 系統會將重新導向 URL 與 302 (已找到) 狀態碼傳回給用戶端。 瀏覽器在重新導向 URL 處提出新要求,該 URL 也會顯示在瀏覽器的網址列中。 因為範例應用程式與重新導向 URL 沒有任何相符規則:
- 第二個要求會從應用程式收到 200 (確定) 回應。
- 回應的本文會顯示重新導向 URL。
「重新導向」URL 時,會對伺服器進行來回行程。
警告
建立重新導向規則時,請務必謹慎。 每向應用程式提出要求時 (包括重新導向後),請評估您的重新導向規則。 因為您有可能會不小心建立無限重新導向迴圈。
原始要求:/redirect-rule/1234/5678
括弧內所含的運算式部分稱為「擷取群組」。 運算式的點 (.
) 表示「比對任何字元」。 星號 (*
) 表示「比對前置字元零或多次」。 因此,擷取群組 (.*)
會擷取 URL 的 1234/5678
最後這兩個路徑區段。 這個單一擷取群組會擷取您在要求 URL 中的 redirect-rule/
之後所提供的任何值。
系統會將擷取的群組以貨幣符號 ($
) 後接擷取序號的形式,插入取代字串中。 使用 $1
可取得第一個擷取群組值、使用 $2
則會取得第二個,依您的 Regex 擷取群組順序以此類推。 在範例應用程式中,重新導向規則 Regex 只有一個擷取的群組,因此在取代字串中只有 $1
這一個插入的群組。 套用規則時,URL 會變成 /redirected/1234/5678
。
將 URL 重新導向至安全端點
使用 AddRedirectToHttps 將 HTTP 要求重新導向至使用 HTTPS 通訊協定的相同主機與路徑。 如果未提供狀態碼,中介軟體會預設為 302 (已找到)。 若未提供連接埠:
- 中介軟體會預設為
null
。 - 配置會變更為
https
(HTTPS 通訊協定),且用戶端會在連接埠 443 上存取資源。
下列範例示範如何將狀態碼設為 301 - Moved Permanently
,並將連接埠變更為 5001。
public void Configure(IApplicationBuilder app)
{
var options = new RewriteOptions()
.AddRedirectToHttps(301, 5001);
app.UseRewriter(options);
}
使用 AddRedirectToHttpsPermanent 將不安全的要求重新導向至使用安全 HTTPS 通訊協定 (在連接埠 443 上) 的相同主機與路徑。 中介軟體會將狀態碼設定為 301 - Moved Permanently
。
public void Configure(IApplicationBuilder app)
{
var options = new RewriteOptions()
.AddRedirectToHttpsPermanent();
app.UseRewriter(options);
}
注意
在不要求額外重新導向規則的情況下重新導向至安全的端點時,建議使用 HTTPS 重新導向中介軟體。 如需詳細資訊,請參閱強制執行 HTTPS 主題。
範例應用程式可以示範如何使用 AddRedirectToHttps
或 AddRedirectToHttpsPermanent
。 將擴充方法新增至 RewriteOptions
。 在任何 URL 位置,向應用程式提出不安全的要求。 關閉瀏覽器自我簽署憑證不受信任的安全性警告,或建立信任憑證的例外狀況。
使用 AddRedirectToHttps(301, 5001)
的原始要求:http://localhost:5000/secure
使用 AddRedirectToHttpsPermanent
的原始要求:http://localhost:5000/secure
URL 重寫
使用 AddRewrite 來建立 URL 重寫的規則。 第一個參數會包含 Regex,以比對傳入的 URL 路徑。 第二個參數是取代字串。 第三個參數 skipRemainingRules: {true|false}
指出中介軟體是否要在套用目前規則時略過其他重寫規則。
public void Configure(IApplicationBuilder app)
{
using (StreamReader apacheModRewriteStreamReader =
File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
File.OpenText("IISUrlRewrite.xml"))
{
var options = new RewriteOptions()
.AddRedirect("redirect-rule/(.*)", "redirected/$1")
.AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
skipRemainingRules: true)
.AddApacheModRewrite(apacheModRewriteStreamReader)
.AddIISUrlRewrite(iisUrlRewriteStreamReader)
.Add(MethodRules.RedirectXmlFileRequests)
.Add(MethodRules.RewriteTextFileRequests)
.Add(new RedirectImageRequests(".png", "/png-images"))
.Add(new RedirectImageRequests(".jpg", "/jpg-images"));
app.UseRewriter(options);
}
app.UseStaticFiles();
app.Run(context => context.Response.WriteAsync(
$"Rewritten or Redirected Url: " +
$"{context.Request.Path + context.Request.QueryString}"));
}
原始要求:/rewrite-rule/1234/5678
運算式開頭的插入號 (^
) 表示,比對會從 URL 路徑的開頭開始。
在先前的重新導向規則範例 redirect-rule/(.*)
中,Regex 的開頭沒有插入號 (^
)。 因此,就算 redirect-rule/
前有任何字元也能成功比對。
路徑 | 比對 |
---|---|
/redirect-rule/1234/5678 |
Yes |
/my-cool-redirect-rule/1234/5678 |
.是 |
/anotherredirect-rule/1234/5678 |
Yes |
^rewrite-rule/(\d+)/(\d+)
重寫規則只會比對開頭為 rewrite-rule/
的路徑。 請注意下表中的比對差異。
路徑 | 比對 |
---|---|
/rewrite-rule/1234/5678 |
是 |
/my-cool-rewrite-rule/1234/5678 |
無 |
/anotherrewrite-rule/1234/5678 |
No |
運算式的 ^rewrite-rule/
部分之後,有 (\d+)/(\d+)
這兩個擷取群組。 \d
表示「比對數字」。 加號 (+
) 表示「比對一或多個前置字元」。 因此,URL 必須包含某個數字,後接斜線與另一個數字。 這些擷取群組會以 $1
和 $2
形式插入重寫的 URL。 重寫規則的取代字串會將擷取的群組放入查詢字串中。 系統會重寫 /rewrite-rule/1234/5678
的要求路徑,以取得位於 /rewritten?var1=1234&var2=5678
的資源。 如果原始要求上有查詢字串,則會在重寫 URL 時予以保留。
這麼做就不需要伺服器的來回行程,即可取得資源。 如果資源存在,就會擷取該資源,並傳回 200 (確定) 狀態碼給用戶端。 因為系統並未重新導向用戶端,因此瀏覽器網址列中的 URL 不會變更。 用戶端也無法偵測到伺服器上發生 URL 重寫作業。
注意
因為比對規則是計算繁複的程序,且會增加應用程式的回應時間,所以請盡可能使用 skipRemainingRules: true
。 如需最快速的應用程式回應:
- 排序重寫規則;從最常比對的規則排到最不常比對的規則。
- 當出現符合項目且不需要任何額外的規則處理時,即略過剩下的規則處理。
Apache mod_rewrite
使用 AddApacheModRewrite 來套用 Apache mod_rewrite 規則。 請確認應用程式已部署規則檔。 如需 mod_rewrite 規則的詳細資訊和範例,請參閱 Apache mod_rewrite。
系統會使用 StreamReader 來讀取 ApacheModRewrite.txt 規則檔案的規則:
public void Configure(IApplicationBuilder app)
{
using (StreamReader apacheModRewriteStreamReader =
File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
File.OpenText("IISUrlRewrite.xml"))
{
var options = new RewriteOptions()
.AddRedirect("redirect-rule/(.*)", "redirected/$1")
.AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
skipRemainingRules: true)
.AddApacheModRewrite(apacheModRewriteStreamReader)
.AddIISUrlRewrite(iisUrlRewriteStreamReader)
.Add(MethodRules.RedirectXmlFileRequests)
.Add(MethodRules.RewriteTextFileRequests)
.Add(new RedirectImageRequests(".png", "/png-images"))
.Add(new RedirectImageRequests(".jpg", "/jpg-images"));
app.UseRewriter(options);
}
app.UseStaticFiles();
app.Run(context => context.Response.WriteAsync(
$"Rewritten or Redirected Url: " +
$"{context.Request.Path + context.Request.QueryString}"));
}
範例應用程式會將要求從 /apache-mod-rules-redirect/(.\*)
重新導向至 /redirected?id=$1
。 回應狀態碼為 302 (已找到)。
# Rewrite path with additional sub directory
RewriteRule ^/apache-mod-rules-redirect/(.*) /redirected?id=$1 [L,R=302]
原始要求:/apache-mod-rules-redirect/1234
中介軟體可支援下列 Apache mod_rewrite 伺服器變數:
- CONN_REMOTE_ADDR
- HTTP_ACCEPT
- HTTP_CONNECTION
- HTTP_COOKIE
- HTTP_FORWARDED
- HTTP_HOST
- HTTP_REFERER
- HTTP_USER_AGENT
- HTTPS
- IPV6
- QUERY_STRING
- REMOTE_ADDR
- REMOTE_PORT
- REQUEST_FILENAME
- REQUEST_METHOD
- REQUEST_SCHEME
- REQUEST_URI
- SCRIPT_FILENAME
- SERVER_ADDR
- SERVER_PORT
- SERVER_PROTOCOL
- TIME
- TIME_DAY
- TIME_HOUR
- TIME_MIN
- TIME_MON
- TIME_SEC
- TIME_WDAY
- TIME_YEAR
IIS URL Rewrite Module 規則
若要使用與套用至 IIS URL Rewrite Module 一樣的規則集,請使用 AddIISUrlRewrite。 請確認應用程式已部署規則檔。 在 Windows Server IIS 上執行時,請不要引導中介軟體使用應用程式的 web.config 檔案。 使用 IIS 時,這些規則應該儲存在應用程式的 web.config 檔案外部,以免與 IIS Rewrite Module 發生衝突。 如需 IIS URL Rewrite Module 規則的詳細資訊和範例,請參閱 Using Url Rewrite Module 2.0 (使用 URL Rewrite Module 2.0) 和 URL Rewrite Module Configuration Reference (URL Rewrite Module 組態參考)。
使用 StreamReader 從 IISUrlRewrite.xml
規則檔案讀取規則:
public void Configure(IApplicationBuilder app)
{
using (StreamReader apacheModRewriteStreamReader =
File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
File.OpenText("IISUrlRewrite.xml"))
{
var options = new RewriteOptions()
.AddRedirect("redirect-rule/(.*)", "redirected/$1")
.AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
skipRemainingRules: true)
.AddApacheModRewrite(apacheModRewriteStreamReader)
.AddIISUrlRewrite(iisUrlRewriteStreamReader)
.Add(MethodRules.RedirectXmlFileRequests)
.Add(MethodRules.RewriteTextFileRequests)
.Add(new RedirectImageRequests(".png", "/png-images"))
.Add(new RedirectImageRequests(".jpg", "/jpg-images"));
app.UseRewriter(options);
}
app.UseStaticFiles();
app.Run(context => context.Response.WriteAsync(
$"Rewritten or Redirected Url: " +
$"{context.Request.Path + context.Request.QueryString}"));
}
範例應用程式會將要求從 /iis-rules-rewrite/(.*)
重寫至 /rewritten?id=$1
。 系統會將回應與 200 (確定) 狀態碼傳送給用戶端。
<rewrite>
<rules>
<rule name="Rewrite segment to id querystring" stopProcessing="true">
<match url="^iis-rules-rewrite/(.*)$" />
<action type="Rewrite" url="rewritten?id={R:1}" appendQueryString="false"/>
</rule>
</rules>
</rewrite>
原始要求:/iis-rules-rewrite/1234
如果您具備使用中且已設定伺服器層級規則的 IIS Rewrite Module,但其會以非預期的方式影響應用程式,則您可以停用應用程式的 IIS Rewrite Module 。 如需詳細資訊,請參閱停用 IIS 模組。
不支援的功能
中介軟體不支援下列 IIS URL 重寫模組功能:
- 輸出規則
- 自訂伺服器變數
- 萬用字元
- LogRewrittenUrl
支援的伺服器變數
中介軟體可支援下列 IIS URL Rewrite Module 伺服器變數:
- CONTENT_LENGTH
- CONTENT_TYPE
- HTTP_ACCEPT
- HTTP_CONNECTION
- HTTP_COOKIE
- HTTP_HOST
- HTTP_REFERER
- HTTP_URL
- HTTP_USER_AGENT
- HTTPS
- LOCAL_ADDR
- QUERY_STRING
- REMOTE_ADDR
- REMOTE_PORT
- REQUEST_FILENAME
- REQUEST_URI
注意
您也可以透過 PhysicalFileProvider 取得 IFileProvider。 這種方法可讓重寫規則檔案的位置更有彈性。 請確認重寫規則檔案已部署到您所提供之路徑的伺服器。
PhysicalFileProvider fileProvider = new PhysicalFileProvider(Directory.GetCurrentDirectory());
以方法為基礎的規則
使用 Add 以在方法中實作您自己的規則邏輯。 Add
會公開 RewriteContext,使 HttpContext 可用於您的方法中。 RewriteContext.Result可判斷其他管線處理的執行方式。 請將值設定為下表中描述的其中一個 RuleResult 欄位。
重寫內容結果 | 動作 |
---|---|
RuleResult.ContinueRules (預設值) |
繼續套用規則。 |
RuleResult.EndResponse |
停止套用規則,並傳送回應。 |
RuleResult.SkipRemainingRules |
停止套用規則,並將內容傳送至下一個中介軟體。 |
public void Configure(IApplicationBuilder app)
{
using (StreamReader apacheModRewriteStreamReader =
File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
File.OpenText("IISUrlRewrite.xml"))
{
var options = new RewriteOptions()
.AddRedirect("redirect-rule/(.*)", "redirected/$1")
.AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
skipRemainingRules: true)
.AddApacheModRewrite(apacheModRewriteStreamReader)
.AddIISUrlRewrite(iisUrlRewriteStreamReader)
.Add(MethodRules.RedirectXmlFileRequests)
.Add(MethodRules.RewriteTextFileRequests)
.Add(new RedirectImageRequests(".png", "/png-images"))
.Add(new RedirectImageRequests(".jpg", "/jpg-images"));
app.UseRewriter(options);
}
app.UseStaticFiles();
app.Run(context => context.Response.WriteAsync(
$"Rewritten or Redirected Url: " +
$"{context.Request.Path + context.Request.QueryString}"));
}
範例應用程式示範了一種方法,可將以 .xml
結尾之路徑的要求重新導向到其他位置。 若要求是針對 /file.xml
發出,則要求會重新導向至 /xmlfiles/file.xml
。 狀態碼則設定為 301 - Moved Permanently
。 當瀏覽器對 /xmlfiles/file.xml
提出新的要求時,靜態檔案中介軟體會從 wwwroot/xmlfiles 資料夾將檔案提供給用戶端。 若要重新導向,請明確設定回應的狀態碼。 否則會傳回 200 (確定) 狀態碼,用戶端上也不會發生重新導向。
RewriteRules.cs
:
public static void RedirectXmlFileRequests(RewriteContext context)
{
var request = context.HttpContext.Request;
// Because the client is redirecting back to the same app, stop
// processing if the request has already been redirected.
if (request.Path.StartsWithSegments(new PathString("/xmlfiles")))
{
return;
}
if (request.Path.Value.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
{
var response = context.HttpContext.Response;
response.StatusCode = (int) HttpStatusCode.MovedPermanently;
context.Result = RuleResult.EndResponse;
response.Headers[HeaderNames.Location] =
"/xmlfiles" + request.Path + request.QueryString;
}
}
這種方法也能重寫要求。 範例應用程式示範了如何重寫任何文字檔要求的路徑,以從 wwwroot 資料夾提供 file.txt 文字檔。 靜態檔案中介軟體會根據更新的要求路徑來提供檔案:
public void Configure(IApplicationBuilder app)
{
using (StreamReader apacheModRewriteStreamReader =
File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
File.OpenText("IISUrlRewrite.xml"))
{
var options = new RewriteOptions()
.AddRedirect("redirect-rule/(.*)", "redirected/$1")
.AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
skipRemainingRules: true)
.AddApacheModRewrite(apacheModRewriteStreamReader)
.AddIISUrlRewrite(iisUrlRewriteStreamReader)
.Add(MethodRules.RedirectXmlFileRequests)
.Add(MethodRules.RewriteTextFileRequests)
.Add(new RedirectImageRequests(".png", "/png-images"))
.Add(new RedirectImageRequests(".jpg", "/jpg-images"));
app.UseRewriter(options);
}
app.UseStaticFiles();
app.Run(context => context.Response.WriteAsync(
$"Rewritten or Redirected Url: " +
$"{context.Request.Path + context.Request.QueryString}"));
}
RewriteRules.cs
:
public static void RewriteTextFileRequests(RewriteContext context)
{
var request = context.HttpContext.Request;
if (request.Path.Value.EndsWith(".txt", StringComparison.OrdinalIgnoreCase))
{
context.Result = RuleResult.SkipRemainingRules;
request.Path = "/file.txt";
}
}
以 IRule 為基礎的規則
利用 Add 來使用類別中實作 IRule 介面的專屬規則邏輯。 相較於使用以方法為基礎的規則方法,IRule
提供了更高彈性。 您的實作類別可以包含建構函式,以便您在其中傳入 ApplyRule 方法的參數。
public void Configure(IApplicationBuilder app)
{
using (StreamReader apacheModRewriteStreamReader =
File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
File.OpenText("IISUrlRewrite.xml"))
{
var options = new RewriteOptions()
.AddRedirect("redirect-rule/(.*)", "redirected/$1")
.AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
skipRemainingRules: true)
.AddApacheModRewrite(apacheModRewriteStreamReader)
.AddIISUrlRewrite(iisUrlRewriteStreamReader)
.Add(MethodRules.RedirectXmlFileRequests)
.Add(MethodRules.RewriteTextFileRequests)
.Add(new RedirectImageRequests(".png", "/png-images"))
.Add(new RedirectImageRequests(".jpg", "/jpg-images"));
app.UseRewriter(options);
}
app.UseStaticFiles();
app.Run(context => context.Response.WriteAsync(
$"Rewritten or Redirected Url: " +
$"{context.Request.Path + context.Request.QueryString}"));
}
為了滿足若干條件,系統會檢查範例應用程式中的 extension
和 newPath
參數值。 extension
必須包含值,且該值必須是 .png
、.jpg
或 .gif。 如果 newPath
無效,就會擲回 ArgumentException。 若要求是針對 image.png
發出,則要求會重新導向至 /png-images/image.png
。 若要求是針對 image.jpg
發出,則要求會重新導向至 /jpg-images/image.jpg
。 狀態碼會設定為 301 - Moved Permanently
,而 context.Result
會設定為停止處理規則並傳送回應。
public class RedirectImageRequests : IRule
{
private readonly string _extension;
private readonly PathString _newPath;
public RedirectImageRequests(string extension, string newPath)
{
if (string.IsNullOrEmpty(extension))
{
throw new ArgumentException(nameof(extension));
}
if (!Regex.IsMatch(extension, @"^\.(png|jpg|gif)$"))
{
throw new ArgumentException("Invalid extension", nameof(extension));
}
if (!Regex.IsMatch(newPath, @"(/[A-Za-z0-9]+)+?"))
{
throw new ArgumentException("Invalid path", nameof(newPath));
}
_extension = extension;
_newPath = new PathString(newPath);
}
public void ApplyRule(RewriteContext context)
{
var request = context.HttpContext.Request;
// Because we're redirecting back to the same app, stop
// processing if the request has already been redirected
if (request.Path.StartsWithSegments(new PathString(_newPath)))
{
return;
}
if (request.Path.Value.EndsWith(_extension, StringComparison.OrdinalIgnoreCase))
{
var response = context.HttpContext.Response;
response.StatusCode = (int) HttpStatusCode.MovedPermanently;
context.Result = RuleResult.EndResponse;
response.Headers[HeaderNames.Location] =
_newPath + request.Path + request.QueryString;
}
}
}
原始要求:/image.png
原始要求:/image.jpg
Regex 範例
目標 | Regex 字串及 比對範例 |
取代字串及 輸出範例 |
---|---|---|
將路徑重寫成查詢字串 | ^path/(.*)/(.*) /path/abc/123 |
path?var1=$1&var2=$2 /path?var1=abc&var2=123 |
移除斜線 | (.*)/$ /path/ |
$1 /path |
強制使用斜線 | (.*[^/])$ /path |
$1/ /path/ |
避免重寫特定的要求 | ^(.*)(?<!\.axd)$ 或 ^(?!.*\.axd$)(.*)$ 是: /resource.htm 否: /resource.axd |
rewritten/$1 /rewritten/resource.htm /resource.axd |
重新排列 URL 區段 | path/(.*)/(.*)/(.*) path/1/2/3 |
path/$3/$2/$1 path/3/2/1 |
取代 URL 區段 | ^(.*)/segment2/(.*) /segment1/segment2/segment3 |
$1/replaced/$2 /segment1/replaced/segment3 |
本文件介紹 URL 重寫,並提供如何在 ASP.NET Core 應用程式中使用 URL 重寫中介軟體的指示。
URL 重寫是指根據一或多個預先定義的規則來修改要求 URL 的動作。 URL 重寫會在資源位置和位址之間建立一個抽象層,讓位置和位址不那麼緊密連結。 下列幾種情況非常適合使用 URL 重寫:
- 暫時或永久性移動/取代伺服器資源,並為這些資源保持穩定的定位器時。
- 跨不同應用程式或跨單一應用程式各區域來分割要求處理作業。
- 移除、新增或重組傳入要求的 URL 區段。
- 針對搜尋引擎最佳化 (SEO) 來將公開 URL 最佳化。
- 允許使用易懂的公開 URL,來協助訪客預測要求資源所傳回的內容。
- 將不安全的要求重新導向至安全的端點。
- 防止直接連結,即外部網站透過將另一個網站的資產連結至本身的內容,來盜用該網站擁有的靜態資產。
注意
URL 重寫可能會降低應用程式的效能。 如果可行的話,請限制規則的數目與複雜程度。
檢視或下載範例程式碼 \(英文\) (如何下載)
URL 重新導向和 URL 重寫
乍看之下,「URL 重新導向」和「URL 重寫」在用語上的差異不太明顯,但卻對提供資源給用戶端方面有重要的影響。 ASP.NET Core 的 URL 重寫中介軟體可以同時滿足這兩種需求。
「URL 重新導向」牽涉有關用戶端的作業,其會指示用戶端到其他位址存取資源,而非用戶端原本要求的位址。 這項作業需要伺服器的來回行程。 當用戶端提出新的資源要求時,系統會將重新導向 URL 傳回給用戶端,並顯示在瀏覽器的網址列中。
若 /resource
重新導向 至 /different-resource
,則伺服器會回應用戶端應前往 /different-resource
取得資源,並提供狀態碼,指出重新導向為暫時性或永久性。
將要求重新導向至不同的 URL 時,可以透過指定具狀態的回應,來指出該重新導向為永久性或暫時性:
如果資源具有新的永久 URL,且您希望指示用戶端以後對該資源的所有請求都應使用新 URL,請使用
301 - Moved Permanently
狀態碼。 用戶端收到 301 狀態碼時,可能會快取並重複使用回應。若重新導向為暫時性或很有可能變更時,請使用 302 (已找到) 狀態碼。 302 狀態碼會指示用戶端不要儲存 URL 並在之後使用。
如需狀態碼的詳細資訊,請參閱 RFC 9110:狀態碼定義。
「URL 重寫」伺服器端作業,會從用戶端要求以外的其他資源位址提供資源。 重寫 URL 並不需要伺服器的來回行程。 系統不會將重寫的 URL 傳回用戶端,也不會顯示在瀏覽器的網址列中。
若 /resource
「重寫」至 /different-resource
,則伺服器會於「內部」擷取資源,並在 /different-resource
傳回資源。
雖然用戶端或許可以在重寫的 URL 處擷取資源,但用戶端並不會在提出要求與接收回應時得知資源位於重寫的 URL 處。
URL 重寫範例應用程式
您可以利用範例應用程式,來探索 URL 重寫中介軟體的功能。 範例應用程式會套用重新導向與重寫規則,還會示範多種情況下的重新導向或重寫 URL。
URL 重寫中介軟體的使用時機
當您無法使用以下方法時,請使用重寫中介軟體:
此外也請在應用程式裝載於 HTTP.sys 伺服器 (之前稱為 WebListener) 時使用中介軟體。
使用 IIS、Apache 和 Nginx 所提供伺服器型 URL 重寫技術的主要原因包括:
中介軟體不支援這些模組的完整功能。
有部分伺服器模組的功能不適用於 ASP.NET Core 專案,例如 IIS Rewrite Module 規則的
IsFile
和IsDirectory
條件約束。 在這些情況下,請改為使用中介軟體。中介軟體的效能可能比不上模組的效能時。
如果要確實得知哪種方法會降低最多效能,或是降低的效能可以忽略的話,進行效能評定是唯一方法。
套件
若要在您的專案中包含中介軟體,請在包含 Microsoft.AspNetCore.Rewrite 套件的專案檔中,將套件參考新增至 Microsoft.AspNetCore.App 中繼套件。
不使用 Microsoft.AspNetCore.App
中繼套件時,請將專案參考新增至 Microsoft.AspNetCore.Rewrite
套件。
延伸模組和選項
若要建立 URL 重寫和重新導向規則,您可以針對每個重寫規則的擴充方法建立 RewriteOptions 類別的執行個體。 依據所需的處理順序來鏈結多個規則。 系統會使用 UseRewriter 將 RewriteOptions
新增至要求管線,並將其傳入 URL 重寫中介軟體:
public void Configure(IApplicationBuilder app)
{
using (StreamReader apacheModRewriteStreamReader =
File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
File.OpenText("IISUrlRewrite.xml"))
{
var options = new RewriteOptions()
.AddRedirect("redirect-rule/(.*)", "redirected/$1")
.AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
skipRemainingRules: true)
.AddApacheModRewrite(apacheModRewriteStreamReader)
.AddIISUrlRewrite(iisUrlRewriteStreamReader)
.Add(MethodRules.RedirectXmlFileRequests)
.Add(MethodRules.RewriteTextFileRequests)
.Add(new RedirectImageRequests(".png", "/png-images"))
.Add(new RedirectImageRequests(".jpg", "/jpg-images"));
app.UseRewriter(options);
}
app.UseStaticFiles();
app.Run(context => context.Response.WriteAsync(
$"Rewritten or Redirected Url: " +
$"{context.Request.Path + context.Request.QueryString}"));
}
將非 www 重新導向 www
有三個選項可讓應用程式將非 www
要求重新導向 www
:
AddRedirectToWwwPermanent:如果要求不是
www
,將要求永久重新導向至www
子網域。 使用 Status308PermanentRedirect 狀態代碼重新導向。AddRedirectToWww:如果傳入要求不是
www
,將要求重新導向至www
子網域。 使用 Status307TemporaryRedirect 狀態代碼重新導向。 多載可讓您提供回應的狀態代碼。 請使用 StatusCodes 類別的欄位來進行狀態碼指派。
URL 重新導向
使用 AddRedirect 將要求重新導向。 第一個參數會包含您的 Regex,以比對傳入的 URL 路徑。 第二個參數是取代字串。 第三個參數 (如果有的話) 會指定狀態碼。 如果您未指定狀態碼,則狀態碼會預設為 302 (已找到),表示已暫時移動或取代資源。
public void Configure(IApplicationBuilder app)
{
using (StreamReader apacheModRewriteStreamReader =
File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
File.OpenText("IISUrlRewrite.xml"))
{
var options = new RewriteOptions()
.AddRedirect("redirect-rule/(.*)", "redirected/$1")
.AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
skipRemainingRules: true)
.AddApacheModRewrite(apacheModRewriteStreamReader)
.AddIISUrlRewrite(iisUrlRewriteStreamReader)
.Add(MethodRules.RedirectXmlFileRequests)
.Add(MethodRules.RewriteTextFileRequests)
.Add(new RedirectImageRequests(".png", "/png-images"))
.Add(new RedirectImageRequests(".jpg", "/jpg-images"));
app.UseRewriter(options);
}
app.UseStaticFiles();
app.Run(context => context.Response.WriteAsync(
$"Rewritten or Redirected Url: " +
$"{context.Request.Path + context.Request.QueryString}"));
}
在啟用開發人員工具的瀏覽器中,使用 /redirect-rule/1234/5678
路徑提出範例應用程式的要求。 Regex 會比對 redirect-rule/(.*)
上的要求路徑,並會將路徑取代為 /redirected/1234/5678
。 系統會將重新導向 URL 與 302 (已找到) 狀態碼傳回給用戶端。 瀏覽器在重新導向 URL 處提出新要求,該 URL 也會顯示在瀏覽器的網址列中。 因為範例應用程式與重新導向 URL 沒有任何相符規則:
- 第二個要求會從應用程式收到 200 (確定) 回應。
- 回應的本文會顯示重新導向 URL。
「重新導向」URL 時,會對伺服器進行來回行程。
警告
建立重新導向規則時,請務必謹慎。 每向應用程式提出要求時 (包括重新導向後),請評估您的重新導向規則。 因為您有可能會不小心建立無限重新導向迴圈。
原始要求:/redirect-rule/1234/5678
括弧內所含的運算式部分稱為「擷取群組」。 運算式的點 (.
) 表示「比對任何字元」。 星號 (*
) 表示「比對前置字元零或多次」。 因此,擷取群組 (.*)
會擷取 URL 的 1234/5678
最後這兩個路徑區段。 這個單一擷取群組會擷取您在要求 URL 中的 redirect-rule/
之後所提供的任何值。
系統會將擷取的群組以貨幣符號 ($
) 後接擷取序號的形式,插入取代字串中。 使用 $1
可取得第一個擷取群組值、使用 $2
則會取得第二個,依您的 Regex 擷取群組順序以此類推。 在範例應用程式中,重新導向規則 Regex 只有一個擷取的群組,因此在取代字串中只有 $1
這一個插入的群組。 套用規則時,URL 會變成 /redirected/1234/5678
。
將 URL 重新導向至安全端點
使用 AddRedirectToHttps 將 HTTP 要求重新導向至使用 HTTPS 通訊協定的相同主機與路徑。 如果未提供狀態碼,中介軟體會預設為 302 (已找到)。 若未提供連接埠:
- 中介軟體會預設為
null
。 - 配置會變更為
https
(HTTPS 通訊協定),且用戶端會在連接埠 443 上存取資源。
下列範例示範如何將狀態碼設為 301 - Moved Permanently
,並將連接埠變更為 5001。
public void Configure(IApplicationBuilder app)
{
var options = new RewriteOptions()
.AddRedirectToHttps(301, 5001);
app.UseRewriter(options);
}
使用 AddRedirectToHttpsPermanent 將不安全的要求重新導向至使用安全 HTTPS 通訊協定 (在連接埠 443 上) 的相同主機與路徑。 中介軟體會將狀態碼設定為 301 - Moved Permanently
。
public void Configure(IApplicationBuilder app)
{
var options = new RewriteOptions()
.AddRedirectToHttpsPermanent();
app.UseRewriter(options);
}
注意
在不要求額外重新導向規則的情況下重新導向至安全的端點時,建議使用 HTTPS 重新導向中介軟體。 如需詳細資訊,請參閱強制執行 HTTPS 主題。
範例應用程式可以示範如何使用 AddRedirectToHttps
或 AddRedirectToHttpsPermanent
。 將擴充方法新增至 RewriteOptions
。 在任何 URL 位置,向應用程式提出不安全的要求。 關閉瀏覽器自我簽署憑證不受信任的安全性警告,或建立信任憑證的例外狀況。
使用 AddRedirectToHttps(301, 5001)
的原始要求:http://localhost:5000/secure
使用 AddRedirectToHttpsPermanent
的原始要求:http://localhost:5000/secure
URL 重寫
使用 AddRewrite 來建立 URL 重寫的規則。 第一個參數會包含 Regex,以比對傳入的 URL 路徑。 第二個參數是取代字串。 第三個參數 skipRemainingRules: {true|false}
指出中介軟體是否要在套用目前規則時略過其他重寫規則。
public void Configure(IApplicationBuilder app)
{
using (StreamReader apacheModRewriteStreamReader =
File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
File.OpenText("IISUrlRewrite.xml"))
{
var options = new RewriteOptions()
.AddRedirect("redirect-rule/(.*)", "redirected/$1")
.AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
skipRemainingRules: true)
.AddApacheModRewrite(apacheModRewriteStreamReader)
.AddIISUrlRewrite(iisUrlRewriteStreamReader)
.Add(MethodRules.RedirectXmlFileRequests)
.Add(MethodRules.RewriteTextFileRequests)
.Add(new RedirectImageRequests(".png", "/png-images"))
.Add(new RedirectImageRequests(".jpg", "/jpg-images"));
app.UseRewriter(options);
}
app.UseStaticFiles();
app.Run(context => context.Response.WriteAsync(
$"Rewritten or Redirected Url: " +
$"{context.Request.Path + context.Request.QueryString}"));
}
原始要求:/rewrite-rule/1234/5678
運算式開頭的插入號 (^
) 表示,比對會從 URL 路徑的開頭開始。
在先前的重新導向規則範例 redirect-rule/(.*)
中,Regex 的開頭沒有插入號 (^
)。 因此,就算 redirect-rule/
前有任何字元也能成功比對。
路徑 | 比對 |
---|---|
/redirect-rule/1234/5678 |
Yes |
/my-cool-redirect-rule/1234/5678 |
.是 |
/anotherredirect-rule/1234/5678 |
Yes |
^rewrite-rule/(\d+)/(\d+)
重寫規則只會比對開頭為 rewrite-rule/
的路徑。 請注意下表中的比對差異。
路徑 | 比對 |
---|---|
/rewrite-rule/1234/5678 |
是 |
/my-cool-rewrite-rule/1234/5678 |
無 |
/anotherrewrite-rule/1234/5678 |
No |
運算式的 ^rewrite-rule/
部分之後,有 (\d+)/(\d+)
這兩個擷取群組。 \d
表示「比對數字」。 加號 (+
) 表示「比對一或多個前置字元」。 因此,URL 必須包含某個數字,後接斜線與另一個數字。 這些擷取群組會以 $1
和 $2
形式插入重寫的 URL。 重寫規則的取代字串會將擷取的群組放入查詢字串中。 系統會重寫 /rewrite-rule/1234/5678
的要求路徑,以取得位於 /rewritten?var1=1234&var2=5678
的資源。 如果原始要求上有查詢字串,則會在重寫 URL 時予以保留。
這麼做就不需要伺服器的來回行程,即可取得資源。 如果資源存在,就會擷取該資源,並傳回 200 (確定) 狀態碼給用戶端。 因為系統並未重新導向用戶端,因此瀏覽器網址列中的 URL 不會變更。 用戶端也無法偵測到伺服器上發生 URL 重寫作業。
注意
因為比對規則是計算繁複的程序,且會增加應用程式的回應時間,所以請盡可能使用 skipRemainingRules: true
。 如需最快速的應用程式回應:
- 排序重寫規則;從最常比對的規則排到最不常比對的規則。
- 當出現符合項目且不需要任何額外的規則處理時,即略過剩下的規則處理。
Apache mod_rewrite
使用 AddApacheModRewrite 來套用 Apache mod_rewrite 規則。 請確認應用程式已部署規則檔。 如需 mod_rewrite 規則的詳細資訊和範例,請參閱 Apache mod_rewrite。
系統會使用 StreamReader 來讀取 ApacheModRewrite.txt 規則檔案的規則:
public void Configure(IApplicationBuilder app)
{
using (StreamReader apacheModRewriteStreamReader =
File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
File.OpenText("IISUrlRewrite.xml"))
{
var options = new RewriteOptions()
.AddRedirect("redirect-rule/(.*)", "redirected/$1")
.AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
skipRemainingRules: true)
.AddApacheModRewrite(apacheModRewriteStreamReader)
.AddIISUrlRewrite(iisUrlRewriteStreamReader)
.Add(MethodRules.RedirectXmlFileRequests)
.Add(MethodRules.RewriteTextFileRequests)
.Add(new RedirectImageRequests(".png", "/png-images"))
.Add(new RedirectImageRequests(".jpg", "/jpg-images"));
app.UseRewriter(options);
}
app.UseStaticFiles();
app.Run(context => context.Response.WriteAsync(
$"Rewritten or Redirected Url: " +
$"{context.Request.Path + context.Request.QueryString}"));
}
範例應用程式會將要求從 /apache-mod-rules-redirect/(.\*)
重新導向至 /redirected?id=$1
。 回應狀態碼為 302 (已找到)。
# Rewrite path with additional sub directory
RewriteRule ^/apache-mod-rules-redirect/(.*) /redirected?id=$1 [L,R=302]
原始要求:/apache-mod-rules-redirect/1234
中介軟體可支援下列 Apache mod_rewrite 伺服器變數:
- CONN_REMOTE_ADDR
- HTTP_ACCEPT
- HTTP_CONNECTION
- HTTP_COOKIE
- HTTP_FORWARDED
- HTTP_HOST
- HTTP_REFERER
- HTTP_USER_AGENT
- HTTPS
- IPV6
- QUERY_STRING
- REMOTE_ADDR
- REMOTE_PORT
- REQUEST_FILENAME
- REQUEST_METHOD
- REQUEST_SCHEME
- REQUEST_URI
- SCRIPT_FILENAME
- SERVER_ADDR
- SERVER_PORT
- SERVER_PROTOCOL
- TIME
- TIME_DAY
- TIME_HOUR
- TIME_MIN
- TIME_MON
- TIME_SEC
- TIME_WDAY
- TIME_YEAR
IIS URL Rewrite Module 規則
若要使用與套用至 IIS URL Rewrite Module 一樣的規則集,請使用 AddIISUrlRewrite。 請確認應用程式已部署規則檔。 在 Windows Server IIS 上執行時,請不要引導中介軟體使用應用程式的 web.config 檔案。 使用 IIS 時,這些規則應該儲存在應用程式的 web.config 檔案外部,以免與 IIS Rewrite Module 發生衝突。 如需 IIS URL Rewrite Module 規則的詳細資訊和範例,請參閱 Using Url Rewrite Module 2.0 (使用 URL Rewrite Module 2.0) 和 URL Rewrite Module Configuration Reference (URL Rewrite Module 組態參考)。
使用 StreamReader 從 IISUrlRewrite.xml
規則檔案讀取規則:
public void Configure(IApplicationBuilder app)
{
using (StreamReader apacheModRewriteStreamReader =
File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
File.OpenText("IISUrlRewrite.xml"))
{
var options = new RewriteOptions()
.AddRedirect("redirect-rule/(.*)", "redirected/$1")
.AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
skipRemainingRules: true)
.AddApacheModRewrite(apacheModRewriteStreamReader)
.AddIISUrlRewrite(iisUrlRewriteStreamReader)
.Add(MethodRules.RedirectXmlFileRequests)
.Add(MethodRules.RewriteTextFileRequests)
.Add(new RedirectImageRequests(".png", "/png-images"))
.Add(new RedirectImageRequests(".jpg", "/jpg-images"));
app.UseRewriter(options);
}
app.UseStaticFiles();
app.Run(context => context.Response.WriteAsync(
$"Rewritten or Redirected Url: " +
$"{context.Request.Path + context.Request.QueryString}"));
}
範例應用程式會將要求從 /iis-rules-rewrite/(.*)
重寫至 /rewritten?id=$1
。 系統會將回應與 200 (確定) 狀態碼傳送給用戶端。
<rewrite>
<rules>
<rule name="Rewrite segment to id querystring" stopProcessing="true">
<match url="^iis-rules-rewrite/(.*)$" />
<action type="Rewrite" url="rewritten?id={R:1}" appendQueryString="false"/>
</rule>
</rules>
</rewrite>
原始要求:/iis-rules-rewrite/1234
如果您具備使用中且已設定伺服器層級規則的 IIS Rewrite Module,但其會以非預期的方式影響應用程式,則您可以停用應用程式的 IIS Rewrite Module 。 如需詳細資訊,請參閱停用 IIS 模組。
不支援的功能
與 ASP.NET Core 2.x 一起發行的中介軟體不支援下列 IIS URL Rewrite Module 功能:
- 輸出規則
- 自訂伺服器變數
- 萬用字元
- LogRewrittenUrl
支援的伺服器變數
中介軟體可支援下列 IIS URL Rewrite Module 伺服器變數:
- CONTENT_LENGTH
- CONTENT_TYPE
- HTTP_ACCEPT
- HTTP_CONNECTION
- HTTP_COOKIE
- HTTP_HOST
- HTTP_REFERER
- HTTP_URL
- HTTP_USER_AGENT
- HTTPS
- LOCAL_ADDR
- QUERY_STRING
- REMOTE_ADDR
- REMOTE_PORT
- REQUEST_FILENAME
- REQUEST_URI
注意
您也可以透過 PhysicalFileProvider 取得 IFileProvider。 這種方法可讓重寫規則檔案的位置更有彈性。 請確認重寫規則檔案已部署到您所提供之路徑的伺服器。
PhysicalFileProvider fileProvider = new PhysicalFileProvider(Directory.GetCurrentDirectory());
以方法為基礎的規則
使用 Add 以在方法中實作您自己的規則邏輯。 Add
會公開 RewriteContext,使 HttpContext 可用於您的方法中。 RewriteContext.Result可判斷其他管線處理的執行方式。 請將值設定為下表中描述的其中一個 RuleResult 欄位。
重寫內容結果 | 動作 |
---|---|
RuleResult.ContinueRules (預設值) |
繼續套用規則。 |
RuleResult.EndResponse |
停止套用規則,並傳送回應。 |
RuleResult.SkipRemainingRules |
停止套用規則,並將內容傳送至下一個中介軟體。 |
public void Configure(IApplicationBuilder app)
{
using (StreamReader apacheModRewriteStreamReader =
File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
File.OpenText("IISUrlRewrite.xml"))
{
var options = new RewriteOptions()
.AddRedirect("redirect-rule/(.*)", "redirected/$1")
.AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
skipRemainingRules: true)
.AddApacheModRewrite(apacheModRewriteStreamReader)
.AddIISUrlRewrite(iisUrlRewriteStreamReader)
.Add(MethodRules.RedirectXmlFileRequests)
.Add(MethodRules.RewriteTextFileRequests)
.Add(new RedirectImageRequests(".png", "/png-images"))
.Add(new RedirectImageRequests(".jpg", "/jpg-images"));
app.UseRewriter(options);
}
app.UseStaticFiles();
app.Run(context => context.Response.WriteAsync(
$"Rewritten or Redirected Url: " +
$"{context.Request.Path + context.Request.QueryString}"));
}
範例應用程式示範了一種方法,可將以 .xml
結尾之路徑的要求重新導向到其他位置。 若要求是針對 /file.xml
發出,則要求會重新導向至 /xmlfiles/file.xml
。 狀態碼則設定為 301 - Moved Permanently
。 當瀏覽器對 /xmlfiles/file.xml
提出新的要求時,靜態檔案中介軟體會從 wwwroot/xmlfiles 資料夾將檔案提供給用戶端。 若要重新導向,請明確設定回應的狀態碼。 否則會傳回 200 (確定) 狀態碼,用戶端上也不會發生重新導向。
RewriteRules.cs
:
public static void RedirectXmlFileRequests(RewriteContext context)
{
var request = context.HttpContext.Request;
// Because the client is redirecting back to the same app, stop
// processing if the request has already been redirected.
if (request.Path.StartsWithSegments(new PathString("/xmlfiles")))
{
return;
}
if (request.Path.Value.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
{
var response = context.HttpContext.Response;
response.StatusCode = (int) HttpStatusCode.MovedPermanently;
context.Result = RuleResult.EndResponse;
response.Headers[HeaderNames.Location] =
"/xmlfiles" + request.Path + request.QueryString;
}
}
這種方法也能重寫要求。 範例應用程式示範了如何重寫任何文字檔要求的路徑,以從 wwwroot 資料夾提供 file.txt 文字檔。 靜態檔案中介軟體會根據更新的要求路徑來提供檔案:
public void Configure(IApplicationBuilder app)
{
using (StreamReader apacheModRewriteStreamReader =
File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
File.OpenText("IISUrlRewrite.xml"))
{
var options = new RewriteOptions()
.AddRedirect("redirect-rule/(.*)", "redirected/$1")
.AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
skipRemainingRules: true)
.AddApacheModRewrite(apacheModRewriteStreamReader)
.AddIISUrlRewrite(iisUrlRewriteStreamReader)
.Add(MethodRules.RedirectXmlFileRequests)
.Add(MethodRules.RewriteTextFileRequests)
.Add(new RedirectImageRequests(".png", "/png-images"))
.Add(new RedirectImageRequests(".jpg", "/jpg-images"));
app.UseRewriter(options);
}
app.UseStaticFiles();
app.Run(context => context.Response.WriteAsync(
$"Rewritten or Redirected Url: " +
$"{context.Request.Path + context.Request.QueryString}"));
}
RewriteRules.cs
:
public static void RewriteTextFileRequests(RewriteContext context)
{
var request = context.HttpContext.Request;
if (request.Path.Value.EndsWith(".txt", StringComparison.OrdinalIgnoreCase))
{
context.Result = RuleResult.SkipRemainingRules;
request.Path = "/file.txt";
}
}
以 IRule 為基礎的規則
利用 Add 來使用類別中實作 IRule 介面的專屬規則邏輯。 相較於使用以方法為基礎的規則方法,IRule
提供了更高彈性。 您的實作類別可以包含建構函式,以便您在其中傳入 ApplyRule 方法的參數。
public void Configure(IApplicationBuilder app)
{
using (StreamReader apacheModRewriteStreamReader =
File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
File.OpenText("IISUrlRewrite.xml"))
{
var options = new RewriteOptions()
.AddRedirect("redirect-rule/(.*)", "redirected/$1")
.AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
skipRemainingRules: true)
.AddApacheModRewrite(apacheModRewriteStreamReader)
.AddIISUrlRewrite(iisUrlRewriteStreamReader)
.Add(MethodRules.RedirectXmlFileRequests)
.Add(MethodRules.RewriteTextFileRequests)
.Add(new RedirectImageRequests(".png", "/png-images"))
.Add(new RedirectImageRequests(".jpg", "/jpg-images"));
app.UseRewriter(options);
}
app.UseStaticFiles();
app.Run(context => context.Response.WriteAsync(
$"Rewritten or Redirected Url: " +
$"{context.Request.Path + context.Request.QueryString}"));
}
為了滿足若干條件,系統會檢查範例應用程式中的 extension
和 newPath
參數值。 extension
必須包含值,且該值必須是 .png
、.jpg
或 .gif。 如果 newPath
無效,就會擲回 ArgumentException。 若要求是針對 image.png
發出,則要求會重新導向至 /png-images/image.png
。 若要求是針對 image.jpg
發出,則要求會重新導向至 /jpg-images/image.jpg
。 狀態碼會設定為 301 - Moved Permanently
,而 context.Result
會設定為停止處理規則並傳送回應。
public class RedirectImageRequests : IRule
{
private readonly string _extension;
private readonly PathString _newPath;
public RedirectImageRequests(string extension, string newPath)
{
if (string.IsNullOrEmpty(extension))
{
throw new ArgumentException(nameof(extension));
}
if (!Regex.IsMatch(extension, @"^\.(png|jpg|gif)$"))
{
throw new ArgumentException("Invalid extension", nameof(extension));
}
if (!Regex.IsMatch(newPath, @"(/[A-Za-z0-9]+)+?"))
{
throw new ArgumentException("Invalid path", nameof(newPath));
}
_extension = extension;
_newPath = new PathString(newPath);
}
public void ApplyRule(RewriteContext context)
{
var request = context.HttpContext.Request;
// Because we're redirecting back to the same app, stop
// processing if the request has already been redirected
if (request.Path.StartsWithSegments(new PathString(_newPath)))
{
return;
}
if (request.Path.Value.EndsWith(_extension, StringComparison.OrdinalIgnoreCase))
{
var response = context.HttpContext.Response;
response.StatusCode = (int) HttpStatusCode.MovedPermanently;
context.Result = RuleResult.EndResponse;
response.Headers[HeaderNames.Location] =
_newPath + request.Path + request.QueryString;
}
}
}
原始要求:/image.png
原始要求:/image.jpg
Regex 範例
目標 | Regex 字串及 比對範例 |
取代字串及 輸出範例 |
---|---|---|
將路徑重寫成查詢字串 | ^path/(.*)/(.*) /path/abc/123 |
path?var1=$1&var2=$2 /path?var1=abc&var2=123 |
移除斜線 | (.*)/$ /path/ |
$1 /path |
強制使用斜線 | (.*[^/])$ /path |
$1/ /path/ |
避免重寫特定的要求 | ^(.*)(?<!\.axd)$ 或 ^(?!.*\.axd$)(.*)$ 是: /resource.htm 否: /resource.axd |
rewritten/$1 /rewritten/resource.htm /resource.axd |
重新排列 URL 區段 | path/(.*)/(.*)/(.*) path/1/2/3 |
path/$3/$2/$1 path/3/2/1 |
取代 URL 區段 | ^(.*)/segment2/(.*) /segment1/segment2/segment3 |
$1/replaced/$2 /segment1/replaced/segment3 |
其他資源
- 檢視或下載範例程式碼 \(英文\) (如何下載)
- GitHub 上的 RewriteMiddleware 來源
- ASP.NET Core 中的應用程式啟動
- ASP.NET 核心中介軟體
- .NET 中的規則運算式
- 規則運算式語言 - 快速參考
- Apache mod_rewrite
- Using Url Rewrite Module 2.0 (for IIS) (使用 URL Rewrite Module 2.0 (適用於 IIS))
- URL Rewrite Module Configuration Reference (URL Rewrite Module 組態參考)
- Keep a simple URL structure (保持精簡的 URL 結構)
- 10 URL Rewriting Tips and Tricks (10 個重寫 URL 的祕訣與技巧)
- To slash or not to slash (是否要使用斜線)