通过使用应用中的路由信息,路由还能生成映射到终结点的 URL。
要在 ASP.NET Core 2.2 中使用最新路由方案,请在 Startup.ConfigureServices 中为 MVC 服务注册指定兼容性版本:
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
将值设置为 false 以使用先前的路由逻辑:
services.AddMvc(options => options.EnableEndpointRouting = false) .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
路由基础知识
默认传统路由 {controller=Home}/{action=Index}/{id?}:
- 支持基本的描述性路由方案。
- 是基于 UI 的应用的有用起点。
专用情况示例包括:博客和电子商务终结点。
属性路由提供了精心设计 API 的公共终结点布局所需的控制级别。
此支持允许从基本路由配置入手,并在确定应用的资源布局后修改路由。
路由使用“终结点”(Endpoint) 来表示应用中的逻辑终结点。
元数据用于实现横切关注点,该实现基于附加到每个终结点的策略和配置。
路由系统具有以下特征:
-
路由模板语法用于通过标记化路由参数来定义路由。
-
可以使用常规样式和属性样式终结点配置。
-
IRouteConstraint 用于确定 URL 参数是否包含给定的终结点约束的有效值。
-
应用模型(如 MVC/Razor Pages)注册其所有终结点,这些终结点具有可预测的路由方案实现。
-
路由实现会在中间件管道中任何所需位置制定路由决策。
-
路由中间件之后出现的中间件可以检查路由中间件针对给定请求 URI 的终结点决策结果。
-
可以在中间件管道中的任何位置枚举应用中的所有终结点。
-
应用可根据终结点信息使用路由生成 URL(例如,用于重定向或链接),从而避免硬编码 URL,这有助于可维护性。
-
URL 生成是基于支持任意可扩展性的地址:
- 可以使用依赖关系注入 (DI) 在任意位置解析链接生成器 API (LinkGenerator) 以生成 URL。
- 如果无法通过 DI 获得链接生成器 API,则 IUrlHelper 会提供生成 URL 的方法。
路由模板
在我们生成的项目中有一个默认的模板,来匹配路由访问到对的路径:
app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); });
大多数应用通过 MapRoute 或 IRouteBuilder 上定义的一种类似扩展方法来创建路由。任何 IRouteBuilder 扩展方法都会创建 Route 的实例并将其添加到路由集合中。MapRoute 仅添加由 DefaultHandler 处理的路由。
上面这个模板和URL的路径匹配类似,例如访问路径 /Product/Detail/17 对应 Controller = Product 、action = Detail 、 id = 17,这边 id 后面的问号表示可选参数是可空的,controller 和 action 等于= 后面表示默认值,访问路径为 / 即访问这个默认值路径,但是访问默认路径就不能传递 id 了,除非这样 /Home/Index/3 。
如果我们系统中的 id 为整数,我们要求不能为字符串,那么我们可以用如下约束(约束有很多种可以按自己情况设置),也可以进行多个约束用冒号隔开,有多个约束存在需要满足所有约束的哦:
{id:int} // {id:int:min(1)}
路由的匹配也可以用正则表达式匹配:
routeBuilder.MapRoute( "Track Package Route", "package/{operation:regex(^track|create$)}/{id:int}");
我们设置多个路由规则,自上而下以第一个匹配成功为准。
使用RouteBuilder
必须在 Startup.Configure 方法中配置路由:
services.AddRouting();
设置如下路由规则:
var trackPackageRouteHandler = new RouteHandler(context => { var routeValues = context.GetRouteData().Values; return context.Response.WriteAsync( $"Hello! Route values: {string.Join(", ", routeValues)}"); }); var routeBuilder = new RouteBuilder(app, trackPackageRouteHandler); routeBuilder.MapRoute( "Track Package Route", "package/{operation:regex(^track|create$)}/{id:int}"); routeBuilder.MapGet("hello/{name}", context => { var name = context.GetRouteValue("name"); // The route handler when HTTP GET "hello/<anything>" matches // To match HTTP GET "hello/<anything>/<anything>, // use routeBuilder.MapGet("hello/{*name}" return context.Response.WriteAsync($"Hi, {name}!"); }); var routes = routeBuilder.Build(); app.UseRouter(routes);
下表显示了具有给定 URI 的响应。
| URI | 响应 |
|---|---|
|
|
Route values: [operation, create], [id, 3] |
|
|
Route values: [operation, track], [id, -3] |
|
|
Route values: [operation, track], [id, -3] |
|
|
请求失败,不匹配。 |
|
|
Hi, Joe! |
|
|
请求失败,仅匹配 HTTP GET。 |
|
|
请求失败,不匹配。 |
Map[Verb] 方法将使用约束来将路由限制为方法名称中的 HTTP 谓词,像是 MapGet 表示匹配 Get 请求。RouteBuilder 不限于上面演示的,还有 MapDelete 等方法。
保留的路由名称
以下关键字是保留的名称,它们不能用作路由名称或参数:
actionareacontrollerhandlerpage
路由的数据令牌
向单独的处理程序调度请求的功能是缩放应用的大小和复杂性的关键。
由于中间件是基于所选终结点来应用策略,因此任何可能影响调度或安全策略应用的决策都应在路由系统内部制定,这一点很重要。
执行终结点委托时,将根据迄今执行的请求处理将 RouteContext.RouteData 的属性设为适当的值。
这些值通常通过标记 URL 来确定,可用来接受用户输入,或者在应用内作出进一步的调度决策。
此外,存储于 RouteData.DataTokens 中的值可以属于任何类型,与 RouteData.Values 相反,后者必须能够转换为字符串,或从字符串进行转换。
Routers 中的最后一项是匹配的路由处理程序。
routes.MapRoute( name: "us_english_products", template: "en-US/Products/{id}", defaults: new { controller = "Products", action = "Details" }, constraints: new { id = new IntRouteConstraint() }, dataTokens: new { locale = "en-US" });
可以看到RouteData的数据为: