.NetCore MVC中的路由(2)在路由中使用約束
0x00 路由模板中的約束
在配置路由模板時,除了以靜態字元和變數的形式外,還可以通過配置對變數進行約束。實際如果不嫌麻煩的話在路由到的Action中對變數進行檢查也是一種方法,不過對於變數的通用的約束當然是放在路由層面更加合適。而且這樣做會簡化Action的程式碼,使Action更加專注於自身業務,符合AOP的思路。這篇文章主要介紹路由模板中的約束。
0x01 在路由模板中使用約束
在路由模板中使用約束是很簡單的,如下所示:
{controller=Home}/{action=Index}/{id:int?}
這個模板就對id進行了約束,約束是通過冒號(:)實現的,冒號後為約束的內容。Int?代表id約束為int?型別,只有id能被轉換為int?型別模板匹配才算成功。即使用int.TryParse()方法返回true。
Home/Index/123匹配成功,id為123
Home/Index/abc 匹配失敗,因為abc無法轉換為int?
Home/Index匹配成功,此時id為null
和配置預設值一樣,除了直接在模板中配置外,也可以在MapRoute方法中通過傳入引數進行配置:
routes.MapRoute( name: "default", template: "{controller}/{action}/{id?}", defaults: new { controller = "Home", action = "Index", }, constraints: new { id = new IntRouteConstraint() });
效果是一樣的。
如果我們要對一個變數使用多個約束,只需要在約束後面繼續用冒號加約束即可。例如
{controller=Home}/{action=Index}/{id:int:min(0)}
這個模板中id被約束為int型別且最小值為0。同樣的也可以以引數的方式配置:
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) }) });
可以達到同樣的效果,不過直接寫到模板更加簡潔和直觀。
0x02 .NetCore MVC中內建的約束
.NetCore MVC中內建了大量的約束可供使用。其中大多數基於型別的約束都是使用該型別的TryParse()來嘗試轉換變數提取出來的字串,返回ture則通過約束,返回false違反約束。
1.約束為int:int,對應的類為IntRouteConstraint
例子:”{controller=Home}/{action=Index}/{id:int }”
2.約束為float:float,對應的類為FloatRouteConstraint
例子:”{controller=Home}/{action=Index}/{id:float }”
3.約束為long:long,對應的類為LongRouteConstraint
例子:”{controller=Home}/{action=Index}/{id:long}”
4.約束為double:double,對應的類為DoubleRouteConstraint
例子:”{controller=Home}/{action=Index}/{id:double }”
5.約束為decimal:decimal,對應的類為DecimalRouteConstraint
例子:”{controller=Home}/{action=Index}/{id:decimal }”
6.約束為布林值:bool,對應的類為BoolRouteConstraint
例子:”{controller=Home}/{action=Index}/{id:bool}”
7.約束為字母:alpha,對應的類為AlphaRouteConstraint
例子:”{controller=Home}/{action=Index}/{id:alpha}”
8.約束為時間日期:datetime,對應的類為DateTimeRouteConstraint
例子:”{controller=Home}/{action=Index}/{id:datetime}”
9.約束為GUID:guid,對應的類為GuidRouteConstraint
例子:”{controller=Home}/{action=Index}/{id:bool}”
10.約束長度:length(len),對應的類為LengthRouteConstraint(len)
例子:”{controller=Home}/{action=Index}/{id:length(5) }”
11.約束最小長度:minlength(len),對應的類為MinLengthRouteConstraint(len)
例子:”{controller=Home}/{action=Index}/{id:minlength(5)}”
12.約束最大長度:maxlength(len),對應的類為MaxLengthRouteConstraint(len)
例子:”{controller=Home}/{action=Index}/{id:maxlength(10)}”
13.約束長度範圍:length(min,max),對應的類為LengthRouteConstraint(min,max)
例子:”{controller=Home}/{action=Index}/{id:length(5,10)}”
14.約束最小值:min(v),對應的類為MinRouteConstraint(v)
例子:”{controller=Home}/{action=Index}/{id:min(0)}”
15.約束最大值:max(v),對應的類為MaxRouteConstraint(v)
例子:”{controller=Home}/{action=Index}/{id:max(1000)}”
16.約束範圍:range(min,max),對應的類為RangeRouteConstraint(min,max)
例子:”{controller=Home}/{action=Index}/{id:range(0,1000) }”
17.約束正規表示式:regex(exp),對應的類為RegexRouteConstraint(exp)
例子:”{controller:regex(^My.*)=Home }/{action=Index}/{id?}”
0x03 建立自定義約束
除了使用內建的約束外,還可以自定義約束。只要實現IRouteConstraint介面即可。這個介面定義了一個方法:
bool Match( HttpContext httpContext, //Http上下文資訊 IRouter route, //當前正在測試的模板資訊 string routeKey, //當前正在測試約束的變數名稱 RouteValueDictionary values, //模板中變數提取出的值 RouteDirection routeDirection) //路由方向,列舉,值包括IncominRequestg和UrlGeneration
這個方法返回ture說明符合約束,返回false說明違反約束。下面我們通過實現這個介面定義一個約束,把id約束為偶數。
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; } }
然後這樣配置路由來約束id:
routes.MapRoute( name: "default", template: "{controller}/{action}/{id?}", defaults: new { controller = "Home", action = "Index", }, constraints: new { id = new EvenRouteConstraint() });
這樣配置後
空Path路由到HomeController的Index,id為空
Home/Index/12路由到HomeController的Index,id為12,是偶數
Home/Index/13匹配失敗,id為13,不是偶數。
除了使用constraints引數新增自定義路由約束外,也可以把自定義路由約束對映為字串,用於路由模板中。
public void ConfigureServices(IServiceCollection services) { services.Configure<RouteOptions>(options=> { options.ConstraintMap.Add("even",typeof(EvenRouteConstraint)); }); // Add framework services. services.AddMvc(); }
這樣配置後路由模板寫成:
routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id:even?}"); });
可以達到同樣的效果。把約束對映為字串後,還可以方便的在Route特性中使用,例如:
[Route("durow/Test/[action]/{id:even?}")]
0x04 相關程式碼
https://github.com/durow/NetCoreStudy/tree/master/src/RouteStudy
更多內容歡迎訪問我的部落格:http://www.durow.vip