端點路由(Endpoint Routing)最早出現在ASP.NET Core2.2,在ASP.NET Core3.0提升爲一等公民。

Endpoint Routing的動機

在端點路由出現之前,我們一般在請求處理管道的末尾,定義MVC中間件解析路由。這種方式意味着在處理管道中,MVC中間件之前的中間件將無法獲得路由信息。

路由信息對於某些中間件非常有用,比如CORS、認證中間件(認證過程可能會用到路由信息)。

同時端點路由提煉出 端點 概念,解耦路由匹配邏輯、請求分發。

Endpoint Routing中間件

由一對中間件組成:

  1. UseRouting 將路由匹配添加到中間件管道。該中間件查看應用程序中定義的端點集合,並根據請求選擇最佳匹配。
  2. UseEndpoints 將端點執行添加到中間件管道。
    MapGet、MapPost等方法將 處理邏輯連接到路由系統;
    其他方法將 ASP.NET Core框架特性連接到路由系統。
  • MapRazorPages for Razor Pages
  • MapControllers for controllers
  • MapHub< THub> for SignalR
  • MapGrpcService< TService> for gRPC

處於這對中間件上游的 中間件: 始終無法感知 Endpoint;

處於這對中間件之間的 中間件,將會感知到Endpoint,並有能力執行附加處理邏輯;

UseEndpoint是一個終點中間件;

沒有匹配,則進入UseEndpoint之後的中間件。

放置在 UseRoutingUseEndpoints 之間的認證授權中間件可以:

感知被匹配的端點信息;在調度到Endpoint之前,應用授權策略。

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    // Matches request to an endpoint.
    app.UseRouting();

    // Endpoint aware middleware. 
    // Middleware can use metadata from the matched endpoint.
    app.UseAuthentication();
    app.UseAuthorization();

    // Execute the matched endpoint.
    app.UseEndpoints(endpoints =>
    {
        // Configure the Health Check endpoint and require an authorized user.
        endpoints.MapHealthChecks("/healthz").RequireAuthorization();

        // Configure another endpoint, no authorization requirements.
        endpoints.MapGet("/", async context =>
        {
            await context.Response.WriteAsync("Hello World!");
        });
    });
}

以上在 /health 定義了健康檢查,該端點定義了 IAuthorizeData metadata,要求先認證再執行健康檢查。

我們在UseRouting、UseEndpoints之間添加一點口水代碼:感知端點:

app.Use(next => context =>
            {
                var endpoint = context.GetEndpoint();
                if (endpoint is null)
                {
                    return Task.CompletedTask;
                }
                Console.WriteLine($"Endpoint: {endpoint.DisplayName}");

                if (endpoint is RouteEndpoint routeEndpoint)
                {
                    Console.WriteLine("Endpoint has route pattern: " +
                        routeEndpoint.RoutePattern.RawText);
                }

                foreach (var metadata in endpoint.Metadata)
                {
                    Console.WriteLine($"Endpoint has metadata: {metadata}");
                }
                return next(context);
            });

當請求 /healthz 時,感知到 AuthorizeAttribute metadata

故猜想認證授權中間件要對 /healthz 起作用,必然會對這個 AuthorizeAttribute metadata有所反應。

於是翻閱Github AuthorizationMiddleware 3.0源碼:發現確實關注了Endpoint

// ---- 截取自https://github.com/dotnet/aspnetcore/blob/master/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs-----
if (endpoint != null)
{
    context.Items[AuthorizationMiddlewareInvokedWithEndpointKey] = AuthorizationMiddlewareWithEndpointInvokedValue;
}
var authorizeData = endpoint?.Metadata.GetOrderedMetadata<IAuthorizeData>() ?? Array.Empty<IAuthorizeData>();
var policy = await AuthorizationPolicy.CombineAsync(_policyProvider, authorizeData);
if (policy == null)
{
     await _next(context);
     return;
}
var policyEvaluator = context.RequestServices.GetRequiredService<IPolicyEvaluator>();
......

AuthorizeAttribute 確實是實現了 IAuthorizeData 接口。

binggo, 猜想得到源碼驗證。

結論

端點路由:允許ASP.NET Core應用程序在中間件管道的早期確定要調度的端點,

以便後續中間件可以使用該信息來提供當前管道配置無法提供的功能。

這使ASP.NET Core框架更加靈活,強化端點概念,它使路由匹配和解析功能與終結點分發功能脫鉤。

相關文章