.NET Core中的路由約束
背景介紹
上週給大家分享了 ofollow,noindex" target="_blank">Nancy in .NET Core學習筆記 - 路由 之後, 就一直在考慮.NET Core能否實現和Nancy中一樣的路由約束, 最近查閱了一下MSDN及一些國外部落格, 發現.NET Core中已經實現了相同的功能,所以這裡給大家分享一下。
路由約束
路由約束是路由中的一種設定,可以幫助我們限制Url中的引數型別,只有當引數符合約束條件的時候,action才會被啟用並觸發。
比如我們現在有以下2個Url
[GET] /api/posts/{id}[GET] /api/posts/{name}
我們希望當Posts後面的引數是int型別的時候觸發第一個Url所指向action, 引數是string型別的時候觸發第二個Url所指向的action。
在這種場景下,我們就需要使用路由約束。
如何新增路由約束
在.NET Core中有2種新增路由約束的方法。
- 行內約束(Inline Constraint)
- 使用MapRoute方法帶Constraint引數的過載
當路由引擎發現當前的請求Url符合某個路由設定之後,就會去觸發當前路由設定中的所有路由約束,當所有的約束都返回true, 這個路由對應的action就會被啟用。
行內約束(Inline Constraint)
所謂的行內約束,即在路由Url模板中直接定義。定義的方式是在引數後面加冒號,並制定約束型別。
例:
"/api/posts/{id:int}"
所以該方式既可以在MapRoute方法中使用,也可以在路由屬性中使用。
在MapRoute方法中使用
routes.MapRoute("default", "{controller=Home}/{action=Index}/{id:int}");
在路由屬性中使用
[Route("Home/Index/{id:int}")] public string Index(int id) { return "I got " + id.ToString(); }
使用MapRoute方法帶Constraint引數的過載
除了行內約束,我們還可以在Startup.cs的中通過app.UseMvc()方法來新增約束。
例:
using Microsoft.AspNetCore.Routing.Constraints; app.UseMvc(routes => { routes.MapRoute("default", "{controller}/{action}/{id}", new { controller = "Home", action = "Index" }, new { id = new IntRouteConstraint() }); });
.NET Core內建的路由約束
.NET Core已經提供了很多基礎的路由約束,總體上分為3種類型。
- 檢查資料型別的約束
- 檢查資料的值/長度/範圍的約束
- 正則表示式約束
檢查資料型別的約束
約束 | 行內 | Constraint類 | 說明 |
---|---|---|---|
int | {id:int} |
IntRouteConstraint |
只允許int32整數 |
alpha | {id:alpha} |
AlphaRouteConstraint |
只能包含大小寫字母 |
bool | {id:bool} |
BoolRouteConstraint |
只允許布林型別 |
datetime | {id:datetime} |
DateTimeRouteConstraint |
只允許日期格式 |
decimal | {id:decimal} |
DecimalRouteConstraint |
只允許decimal型別 |
double | {id:double} |
DoubleRouteConstraint |
只允許double型別 |
float | {id:float} |
FloatRouteConstraint |
只允許float型別 |
guid | {id:guid} |
GuidRouteConstraint |
只允許guid型別 |
檢查資料的值/長度/範圍的約束
約束 | 行內 | Constraint類 | 說明 |
---|---|---|---|
length(length) | {id:length(12)} |
LengthRouteConstraint |
字串長度限制 |
maxlength(value) | {id:maxlength(8)} |
MaxLengthRouteConstraint |
字串最大長度限制 |
minlength(value) | {id:minlength(4)} |
MinLengthRouteConstraint |
字串最小長度限制 |
range(min,max) | {id:range(18,120)} |
RangeRouteConstraint |
數值範圍限制 |
min(value) | {id:min(18)} |
MinRouteConstraint |
最小數值限制 |
max(value) | {id:max(120)} |
MaxRouteConstraint |
最大數值限制 |
正則表示式約束
約束 | 行內 | Constraint類 | 說明 |
---|---|---|---|
regex(expression) | {ssn:regex(^\d{{3}}-\d{{2}}-\d{{4}}$)}/ |
RegexRouteConstraint |
正則表示式約束 |
自定義路由約束
和Nancy一樣,.NET Core也支援自定義路由約束,我們可以通過實現 IRouteConstraint
介面的 Match
方法來自定義路由約束。
我們舉一個和之前 Nancy in .NET Core學習筆記 - 路由 中的類似的例子。
當前我們有一個 PostController
類,程式碼如下:
[ApiController] public class PostController : ControllerBase { [HttpGet] [Route("~/api/posts/{id:int}")] public IActionResult GetPostById(int id) { return Content("Coming from GetPostById"); } [HttpGet] [Route("~/api/posts/{name:alpha}")] public IActionResult GetPostByName(string name) { return Content("Coming from GetPostByName"); } }
這時候我們新增新的action方法GetPostByEmail, 並追加一個email約束,方法如下:
[HttpGet] [Route("~/api/posts/{email:email}")] public IActionResult GetPostByEmail(string email) { return Content("Coming from GetPostByEmail"); }
我們希望當posts後面的引數是email格式的時候,顯示"Coming from GetPostByEmail"。
這裡我們首先新增一個 EmailConstraint
類,並實現 IRouteConstraint
介面的 Match
方法
public class EmailConstraint : IRouteConstraint { public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection) { if (httpContext == null) throw new ArgumentNullException(nameof(httpContext)); if (route == null) throw new ArgumentNullException(nameof(route)); if (routeKey == null) throw new ArgumentNullException(nameof(routeKey)); if (values == null) throw new ArgumentNullException(nameof(values)); object routeValue; if (values.TryGetValue(routeKey, out routeValue)) { var parameterValueString = Convert.ToString(routeValue, CultureInfo.InvariantCulture); return parameterValueString.Contains("@"); } return false; } }
其中 values.TryGetValue(routeKey, out routeValue)
是嘗試從路由引數列表中,取出當前引數的值, 如果當前值中包含@, 我們就簡單的認為這個Email約束通過, 並返回true。
上述程式碼完成之後,我們開啟Startup.cs檔案, 在 ConfigureServices
方法中, 我們將這個自定義的路由約束新增到約束列表中,並指定當前的約束名稱是email。
public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); services.Configure<RouteOptions>(routeOptions => { routeOptions.ConstraintMap.Add("email", typeof(EmailConstraint)); }); }
最後我們看一下效果, 頁面中正確顯示除了"Coming from GetPostByEmail"。