0x00 路由模板中的約束

在配置路由模板時,除了以靜態(tài)字符和變量的形式外,還可以通過配置對變量進(jìn)行約束。實(shí)際如果不嫌麻煩的話在路由到的Action中對變量進(jìn)行檢查也是一種方法,不過對于變量的通用的約束當(dāng)然是放在路由層面更加合適。而且這樣做會簡化Action的代碼,使Action更加專注于自身業(yè)務(wù),符合AOP的思路。這篇文章主要介紹路由模板中的約束。


0x01 在路由模板中使用約束

在路由模板中使用約束是很簡單的,如下所示:

{controller=Home}/{action=Index}/{id:int?}

這個模板就對id進(jìn)行了約束,約束是通過冒號(:)實(shí)現(xiàn)的,冒號后為約束的內(nèi)容。Int?代表id約束為int?類型,只有id能被轉(zhuǎn)換為int?類型模板匹配才算成功。即使用int.TryParse()方法返回true。

Home/Index/123匹配成功,id123

Home/Index/abc 匹配失敗,因?yàn)?span style=";padding: 0px;line-height: 1.5;font-family: 'Liberation Serif', 'Times New Roman', serif">abc無法轉(zhuǎn)換為int?

Home/Index匹配成功,此時idnull

和配置默認(rèn)值一樣,除了直接在模板中配置外,也可以在MapRoute方法中通過傳入?yún)?shù)進(jìn)行配置:

大學(xué)生就業(yè)培訓(xùn),高中生培訓(xùn),在職人員轉(zhuǎn)行培訓(xùn),企業(yè)團(tuán)訓(xùn)

routes.MapRoute(     name: "default",     template: "{controller}/{action}/{id?}",     defaults: new     {         controller = "Home",         action = "Index",     },     constraints: new     {         id = new IntRouteConstraint() });

大學(xué)生就業(yè)培訓(xùn),高中生培訓(xùn),在職人員轉(zhuǎn)行培訓(xùn),企業(yè)團(tuán)訓(xùn)

效果是一樣的。

如果我們要對一個變量使用多個約束,只需要在約束后面繼續(xù)用冒號加約束即可。例如

{controller=Home}/{action=Index}/{id:int:min(0)}

這個模板中id被約束為int類型且最小值為0。同樣的也可以以參數(shù)的方式配置:

大學(xué)生就業(yè)培訓(xùn),高中生培訓(xùn),在職人員轉(zhuǎn)行培訓(xùn),企業(yè)團(tuán)訓(xùn)

routes.MapRoute(     name: "default",     template: "{controller}/{action}/{id?}",     defaults: new     {         controller = "Home",         action = "Index",     },     constraints: new     {         id = new CompositeRouteConstraint(             new IRouteConstraint[]             {                 new IntRouteConstraint(),                 new MinRouteConstraint(0)             }) });

大學(xué)生就業(yè)培訓(xùn),高中生培訓(xùn),在職人員轉(zhuǎn)行培訓(xùn),企業(yè)團(tuán)訓(xùn)

可以達(dá)到同樣的效果,不過直接寫到模板更加簡潔和直觀。

0x02 .NetCore MVC中內(nèi)建的約束

.NetCore MVC中內(nèi)建了大量的約束可供使用。其中大多數(shù)基于類型的約束都是使用該類型的TryParse()來嘗試轉(zhuǎn)換變量提取出來的字符串,返回ture則通過約束,返回false違反約束。

1.約束為intint,對應(yīng)的類為IntRouteConstraint

例子:{controller=Home}/{action=Index}/{id:int }”

2.約束為floatfloat,對應(yīng)的類為FloatRouteConstraint

例子:{controller=Home}/{action=Index}/{id:float }”

3.約束為longlong,對應(yīng)的類為LongRouteConstraint

例子:{controller=Home}/{action=Index}/{id:long}”

4.約束為doubledouble,對應(yīng)的類為DoubleRouteConstraint

例子:{controller=Home}/{action=Index}/{id:double }”

5.約束為decimaldecimal,對應(yīng)的類為DecimalRouteConstraint

例子:{controller=Home}/{action=Index}/{id:decimal }”

6.約束為布爾值:bool,對應(yīng)的類為BoolRouteConstraint

例子:{controller=Home}/{action=Index}/{id:bool}”

7.約束為字母:alpha,對應(yīng)的類為AlphaRouteConstraint

例子:{controller=Home}/{action=Index}/{id:alpha}”

8.約束為時間日期:datetime,對應(yīng)的類為DateTimeRouteConstraint

例子:{controller=Home}/{action=Index}/{id:datetime}”

9.約束為GUIDguid,對應(yīng)的類為GuidRouteConstraint

例子:{controller=Home}/{action=Index}/{id:bool}”


10.約束長度:length(len),對應(yīng)的類為LengthRouteConstraint(len)

例子:{controller=Home}/{action=Index}/{id:length(5) }”

11.約束最小長度:minlength(len),對應(yīng)的類為MinLengthRouteConstraint(len)

例子:{controller=Home}/{action=Index}/{id:minlength(5)}”

12.約束最大長度:maxlength(len),對應(yīng)的類為MaxLengthRouteConstraint(len)

例子:{controller=Home}/{action=Index}/{id:maxlength(10)}”

13.約束長度范圍:length(min,max),對應(yīng)的類為LengthRouteConstraint(min,max)

例子:{controller=Home}/{action=Index}/{id:length(5,10)}”


14.約束最小值:min(v),對應(yīng)的類為MinRouteConstraint(v)

例子:{controller=Home}/{action=Index}/{id:min(0)}”

15.約束最大值:max(v),對應(yīng)的類為MaxRouteConstraint(v)

例子:{controller=Home}/{action=Index}/{id:max(1000)}”

16.約束范圍:range(min,max),對應(yīng)的類為RangeRouteConstraint(min,max)

例子:{controller=Home}/{action=Index}/{id:range(0,1000) }”


17.約束正則表達(dá)式:regex(exp),對應(yīng)的類為RegexRouteConstraint(exp)

例子:{controller:regex(^My.*)=Home }/{action=Index}/{id?}”

0x03 創(chuàng)建自定義約束

除了使用內(nèi)建的約束外,還可以自定義約束。只要實(shí)現(xiàn)IRouteConstraint接口即可。這個接口定義了一個方法:

bool Match(     HttpContext httpContext,   //Http上下文信息     IRouter route,            //當(dāng)前正在測試的模板信息     string routeKey,           //當(dāng)前正在測試約束的變量名稱     RouteValueDictionary values, //模板中變量提取出的值     RouteDirection routeDirection) //路由方向,枚舉,值包括IncominRequestg和UrlGeneration

這個方法返回ture說明符合約束,返回false說明違反約束。下面我們通過實(shí)現(xiàn)這個接口定義一個約束,把id約束為偶數(shù)。

大學(xué)生就業(yè)培訓(xùn),高中生培訓(xùn),在職人員轉(zhuǎn)行培訓(xùn),企業(yè)團(tuán)訓(xùn)

public class EvenRouteConstraint : IRouteConstraint {     public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection)     {         int id;         if (!int.TryParse(values[routeKey].ToString(), out id))             return false;                      return id % 2 == 0;     } }

大學(xué)生就業(yè)培訓(xùn),高中生培訓(xùn),在職人員轉(zhuǎn)行培訓(xùn),企業(yè)團(tuán)訓(xùn)

然后這樣配置路由來約束id:

大學(xué)生就業(yè)培訓(xùn),高中生培訓(xùn),在職人員轉(zhuǎn)行培訓(xùn),企業(yè)團(tuán)訓(xùn)

routes.MapRoute(     name: "default",     template: "{controller}/{action}/{id?}",     defaults: new     {         controller = "Home",         action = "Index",     },     constraints: new     {         id = new EvenRouteConstraint() });

大學(xué)生就業(yè)培訓(xùn),高中生培訓(xùn),在職人員轉(zhuǎn)行培訓(xùn),企業(yè)團(tuán)訓(xùn)

這樣配置后

Path路由到HomeControllerIndex,id為空

Home/Index/12路由到HomeControllerIndexid12,是偶數(shù)

Home/Index/13匹配失敗,id13,不是偶數(shù)。

除了使用constraints參數(shù)添加自定義路由約束外,也可以把自定義路由約束映射為字符串,用于路由模板中。

大學(xué)生就業(yè)培訓(xùn),高中生培訓(xùn),在職人員轉(zhuǎn)行培訓(xùn),企業(yè)團(tuán)訓(xùn)

public void ConfigureServices(IServiceCollection services) {         services.Configure<RouteOptions>(options=>         {             options.ConstraintMap.Add("even",typeof(EvenRouteConstraint));         });         // Add framework services.         services.AddMvc(); }

大學(xué)生就業(yè)培訓(xùn),高中生培訓(xùn),在職人員轉(zhuǎn)行培訓(xùn),企業(yè)團(tuán)訓(xùn)

這樣配置后路由模板寫成:

routes.MapRoute(     name: "default",     template: "{controller=Home}/{action=Index}/{id:even?}"); });

可以達(dá)到同樣的效果。把約束映射為字符串后,還可以方便的在Route特性中使用,例如:

[Route("durow/Test/[action]/{id:even?}")]