ASP.NET Core URL Rewrite中間件

URL重寫是基於一個或多個預置規則修改請求URL的行爲。URL重寫在資源位置和訪問地址之間建立了一種抽象,這樣兩者之間就減小了緊密的聯繫。URL重寫有多種適用的場景:
•臨時或永久移動或替換服務器資源,同時爲這些資源保持穩定的訪問
•爲不一樣應用程序或同一個應用程序的不一樣區域的拆分請求處理
•根據請求移除、添加、從新組織URL段(segment)
•SEO優化
•容許使用友好的公共URL來幫助人們經過連接預測找到內容
•將不安全的請求重定向到安全端點
•圖片防盜鏈web

  能夠經過多種方式定義改變URL的規則,包括正則表達式、Apache mod_rewrite模塊規則、IIS重寫模塊規則和自定義規則邏輯。本文介紹URL重寫及說明如何在ASP.NET Core應用中使用URL重寫中間件。正則表達式

注意:URL重寫可能會下降應用的性能,您應該儘量的限制規則的數量和規則的複雜性。apache

URL重定向和URL重寫瀏覽器

  從字面意思上看URL重定向和URL重寫的差別並不明顯,但兩者在提供資源給客戶端方面都有重要意義。ASP.NET Core的URL重寫中間件可以同時知足兩者的需求。URL重定向是客戶端操做,指示客戶端在另外一個地址訪問資源,須要額外往返服務器。當客戶端對資源發出請求時,返回到客戶端的重定向URL將顯示在瀏覽器的地址欄中。例如/resource被重定向到/different-resource時:客戶端請求/resource,服務端響應客戶端應在/different-resource獲取資源,其響應的狀態碼會指示重定向是臨時的仍是永久的,而後客戶端會向/different-resource發送一個新請求獲取資源。緩存

  將請求重定向到其餘URL時,能夠指定重定向是永久仍是臨時。301(Moved Permanently)狀態代碼用於代表資源具備新的永久URL,而且但願客戶端未來對該資源的全部請求都應使用新URL。當收到301狀態碼時客戶端能夠緩存響應。302(Found)狀態碼用於臨時重定向,因此客戶端不該該存儲和重用該URL。狀態碼的含義請參考這裏。URL重寫是服務器端操做,用於從不一樣的資源地址提供資源。URL重寫不須要額外的往返服務器,而且重寫後的URL不會返回給客戶端,也不會出如今客戶端的地址欄中。當/resource被重寫爲/different-resource時:客戶端請求/resource,服務端在內部從/different-resource獲取資源並響應給客戶端。儘管客戶端也許能夠從重寫後的URL處獲取資源,但客戶端並不會收到資源存在於重寫後URL的通知。安全

什麼時候使用URL重寫中間件服務器

  當沒法在Windows Server上使用IIS重寫模塊、Apache服務器上的Apache mod_rewrite模塊、Nginx上的URL重寫或應用程序託管在HTTP.sys服務器(之前稱爲WebListener)上時,請使用URL重寫中間件。推薦在IIS,Apache或Nginx中使用基於服務器的URL重寫技術的主要緣由是中間件不支持這些模塊的所有功能,而且中間件的性能可能沒法達到這些模塊的性能。可是,這些服務器重寫模塊的某些功能不適用於ASP.NET Core項目,例如IIS Rewrite模塊的IsFile和IsDirectory。在這些狀況下,請改用中間件。併發

包引用app

  要在項目中使用URL重寫中間件,請添加Microsoft.AspNetCore.Rewrite包的引用。該功能適用於ASP.NET Core 1.1或更高版本的應用程序。ide

配置重寫及重定向規則

  經過RewriteOptions類實例的擴展方法來創建URL重寫和重定向規則,按照你但願處理的順序將這些規則連接起來,而後經過使用app.UseRewriter(options)將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.RedirectXMLRequests)
.Add(new RedirectImageRequests(".png", "/png-images"))
.Add(new RedirectImageRequests(".jpg", "/jpg-images"));

app.UseRewriter(options);
}

app.Run(context => context.Response.WriteAsync(
    $"Rewritten or Redirected Url: " +
    $"{context.Request.Path + context.Request.QueryString}"));

}

URL重定向

  使用AddRedirect方法重定向請求,第一個參數爲匹配請求URL的正則表達式,第二個參數爲替換的文本,第三個參數(若是存在)指定狀態碼,若是未指定狀態碼,默認爲302(Found)。

public void Configure(IApplicationBuilder app)
{
var options = new RewriteOptions()
.AddRedirect("redirect-rule/(.*)", "redirected/$1");

app.UseRewriter(options);

}

  打開瀏覽器的開發者工具,向/redirect-rule/1234/5678發送一個請求。重定向規則中的正則表達式將匹配請求路徑,將路徑替換爲/redirected/1234/5678,服務端將重定向URL和302(Found)狀態代碼發送回客戶端。客戶端基於該URL發送新請求並將該URL顯示到地址欄中,而後客戶端收到一個200(OK)的響應。

警告:新建重定向規則時必定要謹慎,重定向規則將會對應用每個請求都進行匹配,包括重定向後的URL。因此很容易不當心建立一個無限重定向循環。

  發送一個請求:/redirect-rule/1234/5678,響應以下圖:

  重定向規則中正則表達式括號內的部分稱爲捕獲組,表達式中點(.)的含義是匹配任何字符,星號()表示匹配以前的字符零次或者屢次。所以,URL中最後兩段/123/5678被(.)捕獲組所捕獲,URL中位於redirect-rule/以後的任何值都將會被該組捕獲。
在替換字符串中,捕獲組將捕獲的內容注入到($n)符號所在位置,其中$後的數字n表明捕獲的序列號。第一個捕獲組是$1,第二個是$2,以此類推。在上面的例子中,重定向規則中的正則表達式只有一個捕獲組,因此替換字符串中只有一個$1,最終/redirect-rule/1234/5678被替換爲/redirect-rule/1234/5678。

URL重定向到安全站點

  可以使用AddRedirectToHttps方法將不安全的請求重定向到具備安全HTTPS協議的同一主機和路徑,若是未提供狀態碼參數,中間件將使用默認值302(Found)。若是未提供端口號參數,中間件使用默認值null,這意味着客戶端將使用https協議同時從443端口訪問資源,下面的代碼片斷演示如何將重定向狀態碼設爲301(Moved Permanently),同時將端口設爲5001:

public void Configure(IApplicationBuilder app)
{
var options = new RewriteOptions().AddRedirectToHttps(301, 5001);

app.UseRewriter(options);

}

   也可使用AddRedirectToHttpsPermanent方法將不安全的請求重定向到具備安全HTTPS協議的同一主機和路徑(端口443上的https://)。中間件將響應狀態碼設置爲301(Moved Permanently)。

public void Configure(IApplicationBuilder app)
{
var options = new RewriteOptions().AddRedirectToHttpsPermanent();

app.UseRewriter(options);

}

注意:在不須要其餘重定向規則的狀況下重定向到HTTPS時,建議使用HTTPS重定向中間件。請參考這裏

URL重寫

  可以使用AddRewrite方法建立重寫規則,第一個參數爲匹配請求URL的正則表達式,第二個參數是替換字符串,第三個參數skipRemainingRules: {true|false},表示若是當前規則生效是否要跳過其它的重寫規則。

public void Configure(IApplicationBuilder app)
{
var options = new RewriteOptions()
.AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", skipRemainingRules: true);

app.UseRewriter(options);
}

}

  發送一個請求:/rewrite-rule/1234/5678,重定向請求及響應以下圖:

  咱們注意到正則表達式開頭是字符^,它的含義是匹配須要從URL路徑的開頭開始。在以前重定向例子中,正則表達式的開頭並無字符^,所以,路徑中redirect-rule/以前的任何字符均可以成功匹配。

路徑

是否匹配

/redirect-rule/1234/5678

/my-cool-redirect-rule/1234/5678

是 

/anotherredirect-rule/1234/5678

  在重寫規則中,正則表達式^rewrite-rule/(\d+)/(\d+)僅匹配以rewrite-rule/開頭的路徑,請注意兩者之間的區別:

路徑

是否匹配

/rewrite-rule/1234/5678

/my-cool-rewrite-rule/1234/5678

/anotherrewrite-rule/1234/5678

  在正則表達式^rewrite-rule/(\d+)/(\d+)中有兩個捕獲組:(\d+)/(\d+),\d表示匹配一個數字,加號(+)表示匹配以前的字符1次或者屢次。所以,匹配的URL必須包含一個數字,後跟一個正斜槓,後跟另外一個數字。捕獲的內容將會被分別注入到重寫字符串中的$1和$2位置。因此請求URL/rewrite-rule/1234/5678將會被重寫爲/rewritten?var1=1234&var2=5678。若是原始請求中存在查詢字符串,則在重寫URL時會保留該查詢字符串。URL重寫不會有額外的服務器往返。若是資源存在,服務端獲取資源內容並返回給客戶端200(OK)狀態碼。由於客戶端沒有被重定向,因此瀏覽器地址欄中的地址不會改變。就客戶端而言,是感知不到URL重寫的。

注意:儘量使用skipRemainingRules:true參數,由於匹配規則是一個昂貴的過程並增長了應用程序響應時間。爲了更快的響應,請考慮如下建議:
•將重寫規則排序:從最常匹配的規則到最不常匹配的規則
•規則匹配成功以後跳過剩餘的規則

使用Apache mod_rewrite規則

  使用AddApacheModRewrite方法應用Apache mod_rewrite規則,請確保規則文件已隨應用程序部署至服務器。瞭解更多關於Apache mod_rewrite規則,請參考這裏

public void Configure(IApplicationBuilder app)
{   //StreamReader用於從ApacheModRewrite.txt規則文件中讀取規則。
using (StreamReader apacheModRewriteStreamReader =
File.OpenText("ApacheModRewrite.txt"))
{
var options = new RewriteOptions()
.AddApacheModRewrite(apacheModRewriteStreamReader);

app.UseRewriter(options);
}

}

如下爲ApacheModRewrite.txt的內容:

Rewrite path with additional sub directory

RewriteRule ^/apache-mod-rules-redirect/(.) /redirected?id=$1 [L,R=302]
示例應用程序未來自/apache-mod-rules-redirect/(.\
)的請求重定向到/redirected?id=$1,響應碼爲302(Found)。

中間件支持如下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重寫模塊規則

  使用AddIISUrlRewrite方法應用IIS URL重寫規則,請確保規則文件已隨應用程序部署至服務器。在Windows Server IIS上運行時,不要讓中間件直接使用web.config文件,規格文件應該存儲於web.config以外,以免和IIS重寫模塊衝突。瞭解更多關於IIS重寫模塊的規則,請參考這裏和這裏。

public void Configure(IApplicationBuilder app)
{  //StreamReader用於從IISUrlRewrite.xml規則文件中讀取規則
using (StreamReader iisUrlRewriteStreamReader =
File.OpenText("IISUrlRewrite.xml"))
{
var options = new RewriteOptions()
.AddIISUrlRewrite(iisUrlRewriteStreamReader);

app.UseRewriter(options);
}

}

如下爲IISUrlRewrite.xml的內容:

<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/(.*)的請求重寫爲/rewritten?id=$1,響應碼爲200(OK)。

ASP.NET Core 2.x發佈的中間件不支持如下IIS URL重寫模塊功能:
•Outbound Rules
•Custom Server Variables
•Wildcards
•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。這種方法能夠爲重寫規則文件的位置提供更大的靈活性。

PhysicalFileProvider fileProvider = new PhysicalFileProvider(Directory.GetCurrentDirectory());
基於方法的規則

  使用Add(Action<RewriteContext> applyRule)在方法中實現本身的規則邏輯,RewriteContext公開HttpContext以方便在方法中使用,而context.Result決定了如何進行後續的管道處理。以下表:

context.Result

行爲

RuleResult.ContinueRules(默認行爲)

繼續應用後續規則

RuleResult.EndResponse

中止應用規則併發送響應

RuleResult.SkipRemainingRules

中止應用規則併發送上下文(HttpContext)至下箇中間件

public void Configure(IApplicationBuilder app)
{
var options = new RewriteOptions()
.Add(MethodRules.RedirectXMLRequests);

app.UseRewriter(options);

}

//自定義的規則方法
public static void RedirectXMLRequests(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("/xmlfiles")))
{
    return;
}

if (request.Path.Value.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
{
    var response = context.HttpContext.Response;
    response.StatusCode = StatusCodes.Status301MovedPermanently;
    context.Result = RuleResult.EndResponse;
    response.Headers[HeaderNames.Location] = 
        "/xmlfiles" + request.Path + request.QueryString;
}

}

  示例應用程序演示了將.xml結尾的請求路徑重定向的自定義邏輯方法。若是對/file.xml發出請求,則會將其重定向到/xmlfiles/file.xml。響應碼被設置爲301 (Moved Permanently)。對於重定向來講,你必須顯式指定響應的狀態碼,不然響應碼將被默認爲200(OK)且客戶端也不會發生重定向。

  發送一個請求:/file.xml,響應以下圖:

基於IRule接口的規則

  使用Add(IRule)在從IRule派生的類中實現您本身的規則邏輯。使用IRule的方式比使用基於方法的規則方法具備更好的靈活性,派生類能夠包含構造函數,您能夠在其中傳遞ApplyRule方法的參數。

public void Configure(IApplicationBuilder app)
{
var options = new RewriteOptions()
.Add(new RedirectImageRequests(".jpg", "/jpg-images"));

app.UseRewriter(options);

}

public class RedirectImageRequests : IRule
{
private readonly string _extension;
private readonly PathString _newPath;

public RedirectImageRequests(string extension, string newPath)
{
    //此處省略了參數校驗

    _extension = extension;
    _newPath = new PathString(newPath);
}

public void ApplyRule(RewriteContext context)
{
    var request = context.HttpContext.Request;
    if (request.Path.StartsWithSegments(new PathString(_newPath)))
    {
        return;
    }

    if (request.Path.Value.EndsWith(_extension, StringComparison.OrdinalIgnoreCase))
    {
        var response = context.HttpContext.Response;
        response.StatusCode = StatusCodes.Status301MovedPermanently;
        context.Result = RuleResult.EndResponse;
        response.Headers[HeaderNames.Location] = 
            _newPath + request.Path + request.QueryString;
    }
}

}

  發送一個請求:/image.png,響應內容以下:

相關文章
相關標籤/搜索