一、概述
此方案从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两个中间件处理,下面依旧通过一幅图来详细看一下:
二、流程及解析
图一
为了方便查看,依然对几个“重点对象”做了颜色标识(点击图片可以看大图):
1. 路由的初始化配置(图的前两个泳道)
① 一切依然是从Startup开始,而且和旧版一样,是通过UseMvc方法进行配置,传入routes.MapRoute(...)这样的一个或多个配置, 不做赘述。
下面着重说一下后面的流程,看一下MvcApplicationBuilderExtensions中的UseMvc方法:
1 public static IApplicationBuilder UseMvc( 2 this IApplicationBuilder app, 3 Action<IRouteBuilder> configureRoutes) 4 { 5 //此处各种验证,略。。 6 var options = app.ApplicationServices.GetRequiredService<IOptions<MvcOptions>>(); 7 if (options.Value.EnableEndpointRouting) 8 { 9 var mvcEndpointDataSource = app.ApplicationServices 10 .GetRequiredService<IEnumerable<EndpointDataSource>>() 11 .OfType<MvcEndpointDataSource>() 12 .First(); 13 var parameterPolicyFactory = app.ApplicationServices 14 .GetRequiredService<ParameterPolicyFactory>(); 15 16 var endpointRouteBuilder = new EndpointRouteBuilder(app); 17 18 configureRoutes(endpointRouteBuilder); 19 20 foreach (var router in endpointRouteBuilder.Routes) 21 { 22 // Only accept Microsoft.AspNetCore.Routing.Route when converting to endpoint 23 // Sub-types could have additional customization that we can't knowingly convert 24 if (router is Route route && router.GetType() == typeof(Route)) 25 { 26 var endpointInfo = new MvcEndpointInfo( 27 route.Name, 28 route.RouteTemplate, 29 route.Defaults, 30 route.Constraints.ToDictionary(kvp => kvp.Key, kvp => (object)kvp.Value), 31 route.DataTokens, 32 parameterPolicyFactory); 33 mvcEndpointDataSource.ConventionalEndpointInfos.Add(endpointInfo); 34 } 35 else 36 { 37 throw new InvalidOperationException($"Cannot use '{router.GetType().FullName}' with Endpoint Routing."); 38 } 39 } 40 if (!app.Properties.TryGetValue(EndpointRoutingRegisteredKey, out _)) 41 { 42 // Matching middleware has not been registered yet 43 // For back-compat register middleware so an endpoint is matched and then immediately used 44 app.UseEndpointRouting(); 45 } 46 return app.UseEndpoint(); 47 } 48 else 49 { 50 //旧版路由方案 51 } 52 }
② 第6行,这里会获取并判断设置的EnableEndpointRouting的值,若为false,则采用旧版路由,详见上一篇文章;该值默认为true,即采用新版路由。
③ 对应第9行,MvcEndpointDataSource在新版路由中是个非常非常重要的角色,在启动初始化阶段,它完成了路由表存储和转换,此处先用颜色重点标记一下,大家记住它,在后面的流程中详细介绍。
④ 对应第16行,同旧版的RouteBuilder一样,这里会new一个 endpointRouteBuilder,二者都是一个IRouteBuilder,所以也同样调用configureRoutes(endpointRouteBuilder)方法(也就是startup中的配置)获取了一个Route的集合(IList<IRouter>)赋值给endpointRouteBuilder.Routes,这里有个特别该注意的地方if (router is Route route && router.GetType() == typeof(Route)) ,也就是这里只接受route类型,终结点路由系统不支持基于 IRouter的可扩展性,包括从 Route继承。
⑤ 对应第20行,这里对刚获取到的endpointRouteBuilder.Routes进行遍历,转换成了一个MvcEndpointInfo的集和,赋值给mvcEndpointDataSource.ConventionalEndpointInfos。
⑥ 之后就是向管道塞中间件了,这里的处理中间件由原来的RouterMiddleware改为EndpointMiddleware和EndpointRoutingMiddleware。