ASP.NET Core端點路由 作用原理

有態度的小碼甲發表於2020-07-12

端點路由(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定義了健康檢查,該端點定義了IAuthorizeDatametadata,要求先認證再執行健康檢查。


我們在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有所反應。

於是翻閱GithubAuthorizationMiddleware3.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框架更加靈活,強化端點概念,它使路由匹配和解析功能與終結點分發功能脫鉤。

相關文章