asp.net core 路由是怎么工作的呢?
我们已经知道asp.net core 是怎么封装http请求, 并且是通过RequestDelegate委托来处理http请求的。而路由是用来区分不同请求并且将该http请求交由对应的requestDelegate处理。
流程图如下
路由系统的两个重要的中间件分别是【EndpointRoutingMiddleware】和【EndpointMiddleware】
【EndpointRoutingMiddleware】根据用户提供的url匹配最相关的终结点endpoint,然后将该终结点交由EndpointMiddleware 处理。
【EndpointMiddleware】用户可以往终结点列表添加终结点,并配置相关的委托。每个终结点里面都会有一个RequestDelegate表示对请求的处理逻辑。并且这个中间件会最终执行RequestDelegate委托来处理请求。
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// 添加Routing相关服务
// 其实这个服务已经在program.cs里面的 ConfigureWebDefaults 中添加
// 无需手动添加
services.AddRouting();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Hello World!");
});
});
}
}
UseRouting()就是注册EndpointRoutingMiddleware;
UseEndpoints()就是注册EndpointMiddleware,处理执行匹配到的终结点,还能往终结点列表添加额外的终结点,如代码中 endpoints.MapGet(...)
所以UseRouting() 和 UseEndpoints() 必须配合使用且UseRouting要先注册。
public class Endpoint
{
public Endpoint(RequestDelegate requestDelegate, EndpointMetadataCollection? metadata, string? displayName);
public string? DisplayName { get; }
public EndpointMetadataCollection Metadata { get; }
public RequestDelegate RequestDelegate { get; }
public override string? ToString();
}
可以看到一个终结点包含有RequestDelegate委托和Metadata元数据,DisplayName就是该终结点的名称。
这里说一下Endpoint一旦创建就是不可变的,也就是说调用了UseRouting之后,终结点列表就会有一堆终结点,等着用户过来匹配。
所以
app.Use(next => context =>
{
// context.GetEndpoint()?.DisplayName=null 因为还没有终结点列表,所以此刻说啥也匹配不上
Console.WriteLine(#34;1. Endpoint: {context.GetEndpoint()?.DisplayName ?? "null"}");
return next(context);
});
// EndpointRoutingMiddleware 调用 SetEndpoint 来设置终结点,此时就会有终结点列表
app.UseRouting();
app.Use(next => context =>
{
// 如果路由匹配到了终结点,那么此处就不为 null,否则,还是 null
Console.WriteLine(#34;2. Endpoint: {context.GetEndpoint()?.DisplayName ?? "null"}");
return next(context);
});
// EndpointMiddleware 通过 GetEndpoint 方法获取终结点,
// 然后执行该终结点的 RequestDelegate 委托
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", context =>
{
// 匹配到了终结点,肯定不是 null
// 而且如果匹配到了终结点,那么该中间件就是终点中间件,后面的中间件就不会执行了。
Console.WriteLine(#34;3. Endpoint: {context.GetEndpoint()?.DisplayName ?? "null"}");
return Task.CompletedTask;
}).WithDisplayName("Custom Display Name"); // 自定义终结点名称
});
app.Use(next => context =>
{
// 当路由没有匹配到终结点时,才会执行这里
Console.WriteLine(#34;4. Endpoint: {context.GetEndpoint()?.DisplayName ?? "null"}");
return next(context);
});
路由的功能远不止这些,如果路由匹配可以用模式匹配,有一些路由模板,会根据模板生成对应的终结点,还有路由约束,路由优先级等等。
……