ASP.NET Core 2.2 : 十六.扒一扒2.2版更新的新路由方案
ASP.NET Core 从2.2版本开始,采用了一个新的名为Endpoint的路由方案,与原来的方案在使用上差别不大,但从内部运行方式上来说,差别还是很大的。上一篇详细介绍了原版路由方案的运行机制,本文仍然通过一幅图来了解一下新版的运行机制,最后再总结一下二者的异同点。(ASP.NET Core 系列目录)
一、概述
此方案从2.2版本开始,被称作终结点路由(下文以“新版”称呼),它是默认开启的,若想采用原来的方案(<=2.1,下文以原版称呼),可以在AddMvc的时候进行设置
services.AddMvc(option=>option.EnableEndpointRouting = false).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
EnableEndpointRouting 默认为true,也就是启用新的Endpoint方案,设置为false则采用旧版(<=2.1)的路由方案。
在配置方法上来说,系统仍然采用在Startup中的use.Mvc()中配置,而实际上内部的处理中间件已由原来的RouterMiddleware改为EndpointMiddleware和EndpointRoutingMiddleware两个中间件处理,下面依旧通过一幅图来详细看一下:
二、流程及解析
private void UpdateEndpoints() { lock (_lock) { var endpoints = new List<Endpoint>(); StringBuilder patternStringBuilder = null; foreach (var action in _actions.ActionDescriptors.Items) { if (action.AttributeRouteInfo == null) { // In traditional conventional routing setup, the routes defined by a user have a static order // defined by how they are added into the list. We would like to maintain the same order when building // up the endpoints too. // // Start with an order of '1' for conventional routes as attribute routes have a default order of '0'. // This is for scenarios dealing with migrating existing Router based code to Endpoint Routing world. var conventionalRouteOrder = 1; // Check each of the conventional patterns to see if the action would be reachable // If the action and pattern are compatible then create an endpoint with the // area/controller/action parameter parts replaced with literals // // e.g. {controller}/{action} with HomeController.Index and HomeController.Login // would result in endpoints: // - Home/Index // - Home/Login foreach (var endpointInfo in ConventionalEndpointInfos) { // An 'endpointInfo' is applicable if: // 1. it has a parameter (or default value) for 'required' non-null route value // 2. it does not have a parameter (or default value) for 'required' null route value var isApplicable = true; foreach (var routeKey in action.RouteValues.Keys) { if (!MatchRouteValue(action, endpointInfo, routeKey)) { isApplicable = false; break; } } if (!isApplicable) { continue; } conventionalRouteOrder = CreateEndpoints( endpoints, ref patternStringBuilder, action, conventionalRouteOrder, endpointInfo.ParsedPattern, endpointInfo.MergedDefaults, endpointInfo.Defaults, endpointInfo.Name, endpointInfo.DataTokens, endpointInfo.ParameterPolicies, suppressLinkGeneration: false, suppressPathMatching: false); } } else { var attributeRoutePattern = RoutePatternFactory.Parse(action.AttributeRouteInfo.Template); CreateEndpoints( endpoints, ref patternStringBuilder, action, action.AttributeRouteInfo.Order, attributeRoutePattern, attributeRoutePattern.Defaults, nonInlineDefaults: null, action.AttributeRouteInfo.Name, dataTokens: null, allParameterPolicies: null, action.AttributeRouteInfo.SuppressLinkGeneration, action.AttributeRouteInfo.SuppressPathMatching); } } // See comments in DefaultActionDescriptorCollectionProvider. These steps are done // in a specific order to ensure callers always see a consistent state. // Step 1 - capture old token var oldCancellationTokenSource = _cancellationTokenSource; // Step 2 - update endpoints _endpoints = endpoints; // Step 3 - create new change token _cancellationTokenSource = new CancellationTokenSource(); _changeToken = new CancellationChangeToken(_cancellationTokenSource.Token); // Step 4 - trigger old token oldCancellationTokenSource?.Cancel(); } }View Code 本质就是计算出一个个可能被请求的请求终结点,也就是Endpoint。由此可见,如上一篇文章那样想自定义一个handler来处理特殊模板的方式(如 routes.MapRoute("flylolo/{code}/{name}", MyRouteHandler.Handler);)将被忽略掉,因其无法生成 Endpoint,且此种方式完全可以自定义一个中间件来实现,没必要混在路由中。
⑨ 就是用上面生成的Matcher,携带Endpoint列表与请求URL做匹配,并将匹配到的Endpoint赋值给feature.Endpoint。
⑩ 获取feature.Endpoint,若存在则调用其RequestDelegate处理请求httpContext。
三、新版与旧版的异同点总结
简要从应用系统启动和请求处理两个阶段对比说一下两个版本的区别:
1.启动阶段:
这个阶段大部分都差不多,都是通过Startup的app.UseMvc()方法配置一个路由表,一个Route的集合Routes(IList<IRouter>),然后将其简单转换一下
<=2.1: 将Routes转换为RouteCollection
2.2+ : 将Routes转换为List<MvcEndpointInfo>
二者区别不大,虽然名字不同,但本质上还是差不多,都仍可理解为Route的集合的包装。
2.请求处理阶段:
<=2.1: 1. 将请求的URL与RouteCollection中记录的路由模板进行匹配。
2. 找到匹配的Route之后,再根据这个请求的URL判断是否存在对应的Controlled和Action。
3. 若以上均通过,则调用Route的Handler对HttpContext进行处理。
2.2+ : 1. 第一次处理请求时,首先根据启动阶段所配置的路由集合List<MvcEndpointInfo>和_actions.ActionDescriptors.Items(所有的action的信息)做匹配,生成一个列表,这个列表存储了所有可能被匹配的URL模板,如图二,这个列表同样是List<MvcEndpointInfo>,记录了所有可能的URL模式,实际上是列出了一个个可以被访问的详细地址,已经算是最终地址了,即终结点,或许就是为什么叫Endpoint路由的原因。
2.请求的Url和这个生成的表做匹配,找到对应的MvcEndpointInfo。
3. 调用被匹配的MvcEndpointInfo的RequestDelegate方法对请求进行处理。
二者区别就是对于_actions.ActionDescriptors.Items(所有的action的信息)的匹配上,原版是先根据路由模板匹配后,再根据ActionDescriptors判断是否存在对应的Controller和action,而新版是先利用了action信息与路由模板匹配,然后再用请求的URL进行匹配,由于这样的工作只在第一次请求的时候执行,所以虽然没有做执行效率上的测试,但感觉应该是比之前快的。
- ASP.NET Core 2.2 基础知识(十六) SignalR (未完待续)
- Scott Mitchell 的ASP.NET 2.0数据教程之十六::概述插入、更新和删除数据
- ASP.NET Core 2.2 基础知识(十一) ASP.NET Core 模块
- ASP.NET Core 中文文档 第四章 MVC(2.2)模型验证
- 【ASP.NET Core快速入门】(六)配置的热更新、配置的框架设计
- Scott Mitchell 的ASP.NET 2.0数据教程之十六::概述插入、更新和删除数据
- ASP.NET Core 2.2 基础知识(四) URL重写中间件
- 2.4配置的热更新「深入浅出ASP.NET Core系列」
- ASP.NET Core 2.2 基础知识(十二) 发送 HTTP 请求
- 在ASP.NET 2.0中操作数据之十六:概述插入、更新和删除数据
- 在ASP.NET 2.0中操作数据之十六:概述插入、更新和删除数据
- [转]ASP.NET Core--根据方案来限制身份
- 重磅!!!微软发布ASP.NET Core 2.2,先睹为快。
- ASP.NET Core 2.2 基础知识(三) 静态文件
- ASP.NET Core 2.2 基础知识(六) 配置(内含MySql+EF)
- asp.net core-8. 配置的热更新
- [翻译] ASP.NET Core 2.2 正式版发布
- 【ASP.NET Core快速入门】(十六)MVC开发:DbContextSeed初始化
- 学习ASP.NET Core Razor 编程系列十六——排序
- Scott Mitchell 的ASP.NET 2.0数据教程之十六::概述插入、更新和删除数据