1. 程式人生 > >ASP.NET Core 2.2中的Endpoint路由

ASP.NET Core 2.2中的Endpoint路由

int att 2.3 first nta logs fault features 依然

Endpoint路由

在ASP.NET Core 2.2中,新增了一種路由,叫做Endpoint(終結點)路由。本文將以往的路由系統稱為傳統路由

本文通過源碼的方式介紹傳統路由和Endpoint路由部分核心功能和實現方法,具體功能上的差異見官方文檔。

在升級到ASP.NET Core 2.2後,會自動啟用Endpoint路由。如果要恢復以往的實現邏輯,需要加入以下代碼:

services.AddMvc(options => options.EnableEndpointRouting = false)
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

本文分析的源代碼基於ASP.NET Core 2.2.3版本的源代碼。

Endpoint作用

Endpoint路由與傳統路由的區別在於,傳統路由UrlAction對應關系的處理是在UseMvc中做的。我們無法根據Url獲取對應的Action然後進行處理。

Endpoint就是將UrlAction的映射關系從Mvc中拆離,作為獨立使用的中間件。

由此帶來的好處是我們可以在其他的中間件中使用ControllerAction上的一些信息,例如Attruibute

框架也提供了LinkGenerator類來直接根據Endpoint生成鏈接,不再需要HttpContext的信息。

另外也提升了一些RPS(Requests per Second)。

不過目前Endpoint依然是在UseMvc中調用,更多開放的使用方式會在ASP.NET Core 3.0中實現。

啟用Endpoint路由

源代碼見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));
    // ...
}

Endpoint路由實現原理

MvcApplicationBuilderExtensions.cs文件的92-123行的代碼是將所有的Controller中的Action轉換成Endpoint

在129行的UseEndpointRouting中,添加了一個EndpointRoutingMiddleware的中間件,這個中間件就是從所有的Endpoint中找到當前路由對應的Endpoint,然後放到Feature集合中。

在132行的UseEndpoint中,添加了一個EndpointMiddleware中間件,這個中間件是將EndpointRoutingMiddleware中找到的Endpoint取出,根據其中的MetaData信息,找到對應的ControllerAction,並調用。

UseMvc方法裏,UseEndpointRoutingUseEndpoint是連續的兩個中間件,而UseEndpoint是請求的結束,這意味著我們自定義的中間件無法取得Endpoint信息。

但是通過手動調用UseEndpoint,我們還是可以拿到Endpoint路由信息的。

使用示例

下面展示一個使用示例。

定義一個LogAttribute類,並包含一個Message屬性,在Action上聲明使用。

定義一個EndpointTestMiddleware中間件,輸出LogAttributeMessage屬性。

手動調用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的實現原理,並給出了一個使用的示例。

參考鏈接:

  1. [https://devblogs.microsoft.com/aspnet/asp-net-core-2-2-0-preview1-endpoint-routing/]
  2. [https://www.stevejgordon.co.uk/asp-net-core-first-look-at-global-routing-dispatcher]
  3. [https://rolandguijt.com/endpoint-routing-in-asp-net-core-2-2-explained/]

ASP.NET Core 2.2中的Endpoint路由