在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的實現原理,並給出了一個使用的示例。
參考連接: