在ASP.NET Core 2.2中,新增了一种路由,叫作Endpoint
(终结点)路由。本文将以往的路由系统称为传统路由
。git
本文经过源码的方式介绍传统路由和Endpoint
路由部分核心功能和实现方法,具体功能上的差别见官方文档。github
在升级到ASP.NET Core 2.2后,会自动启用Endpoint
路由。若是要恢复以往的实现逻辑,须要加入如下代码:c#
services.AddMvc(options => options.EnableEndpointRouting = false) .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
本文分析的源代码基于ASP.NET Core 2.2.3版本的源代码。app
Endpoint
路由与传统路由的区别在于,传统路由Url
与Action
对应关系的处理是在UseMvc
中作的。咱们没法根据Url
获取对应的Action
而后进行处理。框架
Endpoint
就是将Url
与Action
的映射关系从Mvc
中拆离,做为独立使用的中间件。async
由此带来的好处是咱们能够在其余的中间件中使用Controller
和Action
上的一些信息,例如Attruibute
。ui
框架也提供了LinkGenerator
类来直接根据Endpoint
生成连接,再也不须要HttpContext
的信息。spa
另外也提高了一些RPS(Requests per Second)。code
不过目前Endpoint
依然是在UseMvc
中调用,更多开放的使用方式会在ASP.NET Core 3.0中实现。中间件
源代码见Github。也能够获取源代码到本地看。
在MvcApplicationBuilderExtensions.cs
文件72行的UseMvc
方法中咱们能够看到如下代码:
var options = app.ApplicationServices.GetRequiredService<IOptions<MvcOptions>>(); if (options.Value.EnableEndpointRouting) { ... } else { ... }
if
之中是Endpoint
路由的逻辑,else
是传统路由的逻辑。
而MvcOptions
的构造方法以下所示,EnableEndpointRouting
是经过CompatibilitySwitch
来控制默认值的,这就是CompatibilityVersion.Version_2_2
启用Endpoint
路由的缘由。
public MvcOptions() { // ... _enableEndpointRouting = new CompatibilitySwitch<bool>(nameof(EnableEndpointRouting)); // ... }
在MvcApplicationBuilderExtensions.cs
文件的92-123行的代码是将全部的Controller
中的Action
转换成Endpoint
。
在129行的UseEndpointRouting
中,添加了一个EndpointRoutingMiddleware
的中间件,这个中间件就是从全部的Endpoint
中找到当前路由对应的Endpoint
,而后放到Feature
集合中。
在132行的UseEndpoint
中,添加了一个EndpointMiddleware
中间件,这个中间件是将EndpointRoutingMiddleware
中找到的Endpoint
取出,并调用RequestDelegate
。RequestDelegate
是预处理过的Url
对应的Action
方法。
在UseMvc
方法里,UseEndpointRouting
和UseEndpoint
是连续的两个中间件,而UseEndpoint
是请求的结束,这意味着咱们自定义的中间件没法取得Endpoint
信息。
可是经过手动调用UseEndpointRouting
,咱们仍是能够拿到Endpoint
路由信息的。
下面展现一个使用示例。
定义一个LogAttribute
类,并包含一个Message
属性,在Action
上声明使用。
定义一个EndpointTestMiddleware
中间件,输出LogAttribute
的Message
属性。
手动调用UseEndpointRouting
,而后调用咱们定义的EndpointTestMiddleware
中间件。
// Startup.cs public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.UseEndpointRouting(); app.UseMiddleware<EndpointTestMiddleware>(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); } // EndpointTestMiddleware.cs public class EndpointTestMiddleware { private RequestDelegate _next; public EndpointTestMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext httpContext) { var endpoint = httpContext.Features.Get<IEndpointFeature>()?.Endpoint; if (endpoint == null) { await _next(httpContext); return; } var attruibutes = endpoint.Metadata.OfType<LogAttribute>(); foreach (var attribute in attruibutes) { Debug.WriteLine("------------------------------------------------------------------------"); Debug.WriteLine(attribute.Message); Debug.WriteLine("------------------------------------------------------------------------"); } await _next(httpContext); } } // LogAttribute.cs [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = true)] public sealed class LogAttribute : Attribute { public LogAttribute(string message) { Message = message; } public string Message { get; set; } } // HomeController.cs public class HomeController : Controller { [Log("Index")] public IActionResult Index() { return View(); } [Log("Privacy")] public IActionResult Privacy() { return View(); } }
这样的话,咱们能够在咱们本身的中间件中拿到Endpoint
信息,而后找到Controller
上的LogAttribute
,而后输出Message
。
Endpoint
是ASP.NET Core 2.2中一种新的路由机制,它解决了传统路由难以扩展的问题,解决了传统路由与MVC过于耦合的问题,并提高了必定的RPS。
本文介绍了Endpoint路由,简单分析了Endpoint的实现原理,并给出了一个使用的示例。
参考连接: