端点路由(Endpoint Routing
)最早出现在ASP.NET Core2.2
,在ASP.NET Core3.0
提升为一等公民。
Endpoint Routing
的动机
在端点路由出现之前,我们一般在请求处理管道的末尾,定义MVC中间件解析路由。这种方式意味着在处理管道中,MVC中间件之前的中间件将无法获得路由信息。
路由信息对于某些中间件非常有用,比如CORS、认证中间件(认证过程可能会用到路由信息)。
同时端点路由提炼出端点概念,解耦路由匹配逻辑、请求分发。
Endpoint Routing
中间件
由一对中间件组成:
UseRouting
将路由匹配添加到中间件管道。该中间件查看应用程序中定义的端点集合,并根据请求选择最佳匹配。UseEndpoints
将端点执行添加到中间件管道。MapGet
、MapPost
等方法将 处理逻辑连接到路由系统;其他方法将 ASP.NET Core框架特性连接到路由系统。MapRazorPages
for Razor PagesMapControllers
for controllersMapHub< THub>
for SignalRMapGrpcService< TService>
for gRPC
处于这对中间件上游的 中间件:始终无法感知 Endpoint
;
处于这对中间件之间的 中间件,将会感知到被匹配的Endpoint
,并有能力附加处理逻辑;
UseEndpoints
是一个终点中间件;
没有匹配,则进入UseEndpoints
之后的中间件。
放置在UseRouting
、UseEndpoints
之间的认证授权中间件可以:
感知被匹配的端点信息;在调度到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
,并提取了metadata中的IAuthorizeData
授权信息。
// ---- 截取自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
接口。
bingo, 猜想得到源码验证。
结论
端点路由:允许ASP.NET Core应用程序在中间件管道的早期确定要调度的端点, 以便后续中间件可以使用该信息来提供当前管道配置无法提供的功能。
这使ASP.NET Core框架更加灵活,强化端点概念,它使路由匹配、解析功能与端点调度功能脱钩。