一、概述
此方案从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。