ASP.NET Core URL Rewrite中間件

来源:https://www.cnblogs.com/luohelc/archive/2018/07/20/url_rewrite.html
-Advertisement-
Play Games

URL重寫是基於一個或多個預置規則修改請求URL的行為。URL重寫在資源位置和訪問地址之間創建了一種抽象,這樣二者之間就減少了緊密的聯繫。URL重寫有多種適用的場景: 臨時或永久移動或替換伺服器資源,同時為這些資源保持穩定的訪問 為不同應用程式或同一個應用程式的不同區域的拆分請求處理 根據請求移除、 ...


  URL重寫是基於一個或多個預置規則修改請求URL的行為。URL重寫在資源位置和訪問地址之間創建了一種抽象,這樣二者之間就減少了緊密的聯繫。URL重寫有多種適用的場景:

  • 臨時或永久移動或替換伺服器資源,同時為這些資源保持穩定的訪問
  • 為不同應用程式或同一個應用程式的不同區域的拆分請求處理
  • 根據請求移除、添加、重新組織URL段(segment)
  • SEO優化
  • 允許使用友好的公共URL來幫助人們通過鏈接預測找到內容
  • 將不安全的請求重定向到安全端點
  • 圖片防盜鏈

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

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

 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。在這些情況下,請改用中間件。

包引用

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

配置重寫及重定向規則

  通過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,響應內容如下:

尾語

  本來主要內容來自於微軟官方英文文檔,點擊這裡查看原文,在翻譯過程中,對有些句式和辭彙進行了加工,以期望更加流暢和符合我們的閱讀習慣,其中應該有不少不得體的地方,還請各位大神見諒並指出,如有一絲幫助,萬分榮幸。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 測試 ASP.NET Core API Controller ...
  • 怎麼最簡單盜取別人微信密碼以及知道微信號如何可以盜別人的微信? 社交是我們生活和工作中必不可少的一部分。隨著互聯網時代的來臨,越來越多人選擇了大門不出二門不邁,通過使用微信這種社交軟體來完成溝通與交流。微信是人們在社交過程中不可獲缺的一個軟體,在使用過程中也經常出現微信盜號的事情,在這裡就針對微信盜 ...
  • ...
  • 第一步:製作收款碼圖片(命名.bmp); 第二步:將圖片上傳至博客園文件中,我的博客--管理 上傳文件--上傳成功 第三步:在博客管理後臺中選中設置菜單,找到頁腳html 代碼,嵌入一下html代碼即可 ...
  • 揭曉黑客教程:如何盜別人的微信密碼?怎麼盜取對方的微信號? 隨著互聯網時代的來臨,在我們的周邊越來越多的人使用上了微信。微信的出現讓人們的生活發生了巨大改變,有人使用微信和親朋好友聊天,也有人將其當作了生活中不可獲缺的一項工具。但是正是因為微信的使用日益廣泛,才使得在生活中經常會出現微信密碼被盜取的 ...
  • 恢復內容開始 每次看這些排序都像沒見過一樣,完全理解不了,可是不久前明明瞭解的十分透徹。記下來記下來記下來! 1>>>冒泡排序:相鄰的兩兩相比 把大的(或者小的)放後邊,這樣一輪下來,最下邊的肯定是最大的數(或者最小的數)。重覆arr.length次就能把arr按有序排列了。 2>>>選擇排序:從第 ...
  • Progress控制項 一、 樣式一 我們要實現上圖中的效果,需要如下的操作: 設置控制項的背景色為“235, 235, 235”,如圖1; 圖 1設置界面 獲取和設置背景色的透明度為“50”,如圖 2; 圖 2設置界面 該屬性預設設置為“255”,即不透明;若將該屬性設置為“0”,該控制項的背景色即為全 ...
  • 時光偷走的,永遠都是我們眼皮底下看不見的珍貴。 https://pan.baidu.com/s/14dk-OU2SR0nxXj2bL4bVpQ 首先先看一下源代碼最初的運行效果,是否是自己需要的。 在這裡,三維的面圖是用函數生成的Z軸數據,當然,我們也可以使用圖片的顏色作為Z軸。 準備工作,VS20 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...