ASP.NET Core的路由[5]:內聯路由約束的檢驗

来源:http://www.cnblogs.com/artech/archive/2016/12/26/asp-net-core-routing-05.html
-Advertisement-
Play Games

當某個請求能夠被成功路由的前提是它滿足某個Route對象設置的路由規則,具體來說,當前請求的URL不僅需要滿足路由模板體現的路徑模式,請求還需要滿足Route對象的所有約束。路由系統採用IRouteConstraint介面來表示路由約束,所以我們在接下來的內容中將路由約束統稱為RouteConstr... ...


當某個請求能夠被成功路由的前提是它滿足某個Route對象設置的路由規則,具體來說,當前請求的URL不僅需要滿足路由模板體現的路徑模式,請求還需要滿足Route對象的所有約束。路由系統採用IRouteConstraint介面來表示路由約束,所以我們在接下來的內容中將路由約束統稱為RouteConstraint。 在大部分情況下,約束都是針對路由模板中定義的某個路由參數,其目的在於驗證URL攜帶的某部分的內容是否有效。不過也有一些約束與路由參數無關,這些約束規範往往是除URL之前的其他請求元素,比如前面提到的HttpMethodRouteConstraint檢驗的就是請求採用的方法。 [本文已經同步到《ASP.NET Core框架揭秘》之中]

   1: public interface IRouteConstraint
   2: {
   3:     bool Match(HttpContext httpContext, IRouter route, string routeKey, 
   4:     RouteValueDictionary values, RouteDirection routeDirection);
   5: }

如上面的代碼片段所示,IRouteConstraint介面僅僅定義瞭如下一個唯一的Match方法來定義約束規範。方法的參數分別是代表當前請求上下文的HttpContext、當前Router對象、約束在約束字典中的Key(對於針對路由參數的約束,這個Key就是路由參數的名稱)、從請求URL解析出來的所有路由參數和路由方向(針對入棧請求進行的路由解析還是為了生成URL而進行的路由解析)。

一、預定義RouteConstraint

路由系統定義了一系列原生的RouteConstraint類型,我們可以使用它們解決很多常見的約束問題,即使現有的RouteConstraint類型無法滿足某些特殊的約束需求,我們還可以自定義對應的RouteConstraint類型。對於路由約束的應用,除了直接創建對應的RouteConstraint對象之外,我們知道還可以採用內聯的方式直接在路由模板中定義為某個路由參數定義相應的約束表達式。這些以表達式定義的約束類型其實對應著一種具體的RouteConstraint類型。下表列出了兩者之間的匹配關係。

內聯約束類型

RouteConstraint類型

說明

int

IntRouteConstraint

要求路由參數值可能解析為一個int整數,比如{variable:int}

bool

BoolRouteConstraint

要求參數值可以解析為一個bool值,比如{ variable:bool}

datetime

DateTimeRouteConstraint

要求參數值可以解析為一個DateTime對象(採用CultureInfo. InvariantCulture進行解析),比如{ variable:datetime}

decimal

DecimalRouteConstraint

要求參數值可以解析為一個decimal數字,比如{ variable:decimal}

double

DoubleRouteConstraint

要求參數值可以解析為一個double數字,比如{ variable:double}

float

FloatRouteConstraint

要求參數值可以解析為一個float數字,比如{ variable:float}

guid

GuidRouteConstraint

要求參數值可以解析為一個Guid,比如{ variable:guid}

long

LongRouteConstraint

要求參數值可以解析為一個long整數,比如{ variable:long}

minlength

MinLengthRouteConstraint

要求參數值表示的字元串不於指定的長度{ variable:minlength(5)}

maxlength

MaxLengthRouteConstraint

要求參數值表示的字元串不大於指定的長度,比如{ variable:maxlength(10)}

length

LengthRouteConstraint

要求參數值表示的字元串長度限於指定的區間範圍,比如{ variable:length(5,10)}

min

MinRouteConstraint

要求參數值不於指定的值,比如{ variable:min(5)}

max

MaxRouteConstraint

要求參數值大於指定的值,比如{ variable:max(10)}

range

RangeouteConstraint

要求參數值介於指定的區間範圍,比如{variable:range(5,10)}

alpha

AlphaRouteContraint

要求參數值得所有字元都是字母,比如{variable:alpha}

regex

RegexInlineRouteConstraint

要求參數值表示字元串與指定的正則表達式相匹配,比如{variable:regex(^d{0[0-9]{{2,3}-d{2}-d{4}$)}}}$)}

required

RequiredRouteConstraint

要求參數值不應該是一個空字元串,比如{variable:required}

RangeRouteConstraint

為了讓讀者朋友們對這些RouteConstraint具有更加深刻的理解,我們選擇一個用於限制變數值範圍的RangeRouteConstraint類進行單獨介紹。如下麵的代碼片斷所示,RangeRouteConstraint類型具有兩個長整型的只讀屬性Max和Min,它們分別表示約束範圍的上下限。

   1: public class RangeRouteConstraint : IRouteConstraint
   2: {
   3:     public long Max { get;  }
   4:     public long Min { get; }
   5:     public RangeRouteConstraint(long min, long max)
   6:     {
   7:         this.Min = min;
   8:         this.Max = max;
   9:     }
  10:  
  11:     public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection)
  12:     {
  13:         object value;
  14:         if (values.TryGetValue(routeKey, out value) && value != null)
  15:         {
  16:             long longValue;
  17:             var valueString = Convert.ToString(value, CultureInfo.InvariantCulture);
  18:             if (long.TryParse(valueString, NumberStyles.Integer, CultureInfo.InvariantCulture, out longValue))
  19:             {
  20:                 return longValue >= Min && longValue <= Max;
  21:             }
  22:         }
  23:         return false;
  24:     }
  25: }

具體的約束檢驗實現在Match方法中。具體來說,RangeRouteConstraint根據被檢驗變數的名稱(對應於routeKey參數)從參數values(表示路由檢驗生成的所有路由變數)中提取被驗證的參數值,然後判斷它是否在通過屬性Max和Min表示的數值範圍內。

HttpMethodRouteConstraint

上面介紹的這些預定義的RouteConstraint類型都是對某個路由參數的值加以約束,除此之外還具有一個特殊的名為HttpMethodRouteConstraint的約束。我們在上面已經提到過,這個約束並不是應用在具有某個路由參數上,而是應用到整個請求上,它要求匹配的請求必須具有指定的方法。當我們在使用這種約束的時候,一般將對應的Key設置為“httpMethod”。

   1: public class HttpMethodRouteConstraint : IRouteConstraint
   2: {
   3:     public IList<string> AllowedMethods { get; }
   4:  
   5:     public HttpMethodRouteConstraint(params string[] allowedMethods)
   6:     {
   7:         this.AllowedMethods = new List<string>(allowedMethods);
   8:     }
   9:     
  10:     public virtual bool Match(HttpContext httpContext, IRouter route, string routeKey,RouteValueDictionary values, RouteDirection routeDirection)
  11:     {        
  12:         switch (routeDirection)
  13:         {
  14:             case RouteDirection.IncomingRequest:return AllowedMethods.Contains(httpContext.Request.Method, StringComparer.OrdinalIgnoreCase);
  15:  
  16:             case RouteDirection.UrlGeneration:
  17:                 object obj;
  18:                 if (!values.TryGetValue(routeKey, out obj))
  19:                 {
  20:                     return true;
  21:                 }
  22:                 return AllowedMethods.Contains(Convert.ToString(obj), StringComparer.OrdinalIgnoreCase);
  23:  
  24:             default:throw new ArgumentOutOfRangeException(nameof(routeDirection));
  25:         }
  26:     }
  27: }

當我們在創建一個 HttpMethodRouteConstraint對象的時候,需要指定一個允許的HTTP方法列表。對於針對入棧請求的路由解析來說,HttpMethodRouteConstraint會檢驗當前請求採用的方法是否在這個列表之內。如果路由解析是為了生成URL,HttpMethodRouteConstraint會從指定的參數列表中提取指定的HTTP方法,如果這樣的參數存在,則會檢驗這個HTTP方法是否在允許的列表之內,否則意味著不需要針對HTTP方法進行驗證。

二、InlineConstraintResolver

如果在進行路由註冊的時候針對路由變數的約束是直接以內聯表達式的形式定義在路由模板中,所以路由系統需要解析約束表達式來創建對應類型的RouteConstraint對象,這項任務由一個叫做InlineConstraintResolver的對象來完成。所有的InlineConstraintResolver類型實現了具有如下定義的IInlineConstraintResolver介面,定義其中的唯一方法ResolveConstraint實現了約束從字元串表達式到RouteConstraint對象之間的轉換。

   1: public interface IInlineConstraintResolver
   2: {   
   3:     IRouteConstraint ResolveConstraint(string inlineConstraint);
   4: }

路由系統只定義了一個唯一的InlineConstraintResolver類型實現了這個介面,它就是DefaultInlineConstraintResolver類型。如下麵的代碼片斷所示,它具有一個字典類型的欄位_inlineConstraintMap,如表1所示的內聯約束類型與對應RouteConstraint類型之間的映射關係就保存在這個字典中。

   1: public class DefaultInlineConstraintResolver : IInlineConstraintResolver
   2: {
   3:     private readonly IDictionary<string, Type> _inlineConstraintMap;
   4:     public DefaultInlineConstraintResolver(IOptions<RouteOptions> routeOptions)
   5:     {
   6:         _inlineConstraintMap = routeOptions.Value.ConstraintMap;
   7:     }
   8:     public virtual IRouteConstraint ResolveConstraint(string inlineConstraint);
   9: }
  10:  
  11: public class RouteOptions
  12: {
  13:     public IDictionary<string, Type> ConstraintMap { get; set; }
  14:     public bool                      LowercaseUrls { get; set; }
  15:     public bool                      AppendTrailingSlash { get; set; }
  16: }

DefaultInlineConstraintResolver首先根據指定的約束表達式獲得以字元串表示的約束類型和參數列表。通過約束類型,它可以從ConstraintMap屬性表示的映射關係中得到對應的HttpRouteConstraint類型。接下來它根據參數個數得到匹配的構造函數,然後將字元串表示的參數轉換成對應的參數類型並以反射的形式將它們傳入構造函數創建相應的HttpRouteConstraint對象。

對於一個通過指定的路由模板創建的Route對象來說,當它在初始化的時候會利用ServiceProvider採用依賴註入的形式獲取這個InlineConstraintResolver對象來解析定義在路由模板中的內聯約束表達式,並將它們全部轉換成具體的RouteConstraint對象。這意味著在這之前,針對InlineConstraintResolver的服務註冊就以及存在,那麼這個服務是在什麼時候註冊的呢?

當我們在一個ASP.NET Core應用中使用路由功能的時候,除了需要註冊這個RouterMiddleware中間件之外,一般還需要調用ServiceCollection的擴展方法AddRouting註冊一些與路由相關的服務,針對InlineConstraintResolver的服務註冊就實現在這個方法之中。

三、自定義約束

我們可以使用上述這些預定義的RouteConstraint類們完成一些常用的約束檢驗,但是在一些對路由變數具有特殊的約束的應用場景中,我們不得不創建自定義的約束。舉個簡單的例子,如果我們需要對資源提供針對多語言的支持,最好的方式是在請求的URL中提供目標資源所針對的Culture。為了確保包含在URL中的是一個合法有效的Culture,我們最好為此定義相應的約束。

接下來,我們將通過一個簡單的實例來演示如何創建這麼一個用於驗證Culture的自定義約束。不過在這之前我們不妨先來看看使用這個約束最終實現的效果。在本例中我們創建了一個提供基於不同語言資源的Web API,簡單起見,我們僅僅提供針對相應Culture的文本數據。我們利用資源文件來作為文本資源的存儲,如下圖所示,我們在一個ASP.NET Core應用中創建了兩個資源文件Resources.resx(語言文化中性)和Resources.zh.resx(中文),並定義了一個名為“hello”的文本資源條目。

10

如下所示的是整個應用程式的定義。這段程式非常簡單,我們註冊了一個模板為“resources/{lang:culture}/{resourcename:required}”的路由。路由參數{ resourcename }表示獲取的資源條目的名稱(比如“hello”),這是一個必需的路由參數(應用了RequiredRouteConstraint約束)。另一個路由參數{lang}表示指定的語言,約束表達式名稱“culture”對應的就是我們自定義的針對語言文件的約束類型CultureConstraint。也正是因為是一個自定義的路由約束,我們必須將內聯約束表達式名稱和CultureConstraint類型之間的應用,我們在調用ConfigureServices方法中將這樣的映射添加到註冊的RouteOptions之中。

   1: public class Program
   2: {
   3:     public static void Main()
   4:     {
   5:         string template = "resources/{lang:culture}/{resourceName:required}";
   6:  
   7:         Action<IApplicationBuilder> action = app => app
   8:             .UseMiddleware<LocalizationMiddleware>("lang")
   9:             .Run(async context =>
  10:             {
  11:                 var values = context.GetRouteData().Values;
	   

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

-Advertisement-
Play Games
更多相關文章
  • Linux硬體IO的優化簡介 首先簡單介紹下有哪些硬體設備如下(由於硬體種類廠家等各種因素我就不在此多做介紹有興趣的可以自行學習): 1.CPU:中央處理器,是電腦運算控制的核心部件之一,相當於人的大腦。如下圖 2.RAM:記憶體條與CPU溝通的橋梁,很多數據和運算在記憶體中做臨時處理,相當於你在做快 ...
  • 分享一個MySQL分庫備份腳本(原) 開發思路: 1.路徑:規定備份到什麼位置,把路徑(先判斷是否存在,不存在創建一個目錄)先定義好,我的路徑:/mysql/backup,每個備份用壓縮提升效率,帶上時間方便整理 2.取資料庫:抓取資料庫名稱,我用的awk和grep配合取資料庫的名稱(如果想按照表備 ...
  • 自己簡單寫的一個php服務的啟動腳本和大家分享 思路(實現的原理): 1:function模塊+case語句多分支判斷 2:通過添加# chkconfig: 2345 43 89註釋實現開機自啟動(前提是把腳本放入/etc/init.d/目錄下 然後chmod給可執行許可權,然後chkconfig - ...
  • MySQL(或者其它服務)的keepalived高可用監控腳本 開發腳本需求:我們知道,keepalive是基於虛擬ip的存活來判斷是否搶占master的機制的,但是如果我們做了MySQL的keepalived的高可用的時候,就要考慮一種情況的發生,那就是如果機器網卡並沒有斷,二十由於MySQL服務 ...
  • 驗證碼類 使用方法 HTML 視圖JS ...
  • 1 自定義控制項與用戶控制項區別 WinForm中, 用戶控制項(User Control):繼承自 UserControl,主要用於開發 Container 控制項,Container控制項可以添加其他Controls控制項 自定義控制項(Custom Control):繼承自 Control,主要用於開發wi ...
  • 年關將近,整個人已經沒有了工作和寫作的激情,估計這個時候很多人跟我差不多,該相親的相親,該聚會喝酒的聚會喝酒,總之就是沒有了幹活的心思(我有很多想法,但就是叫不動我的手腳,所以我只能看著別人在做我想做的事,吃我想吃的東西。)。本人由上個月的每周四五篇,到現在的文章縮短到每周一篇,說個實話,現在的一篇 ...
  • .NET Core和 .NET 4.6中 的C# 6/7 中的編譯器Roslyn 一個重要的特性就是"Compiler as a Service",簡單的講,就是就是將編譯器開放為一種可在代碼中調用的服務, 通常在工作流引擎 或是規則引擎中都需要一項功能是計算表達式,這樣的任務很容易就用Roslyn... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...