「Asp.net core」 路由和终结点浅析

小编:迷魂雪 更新时间:2022-05-02

流程

asp.net core 路由是怎么工作的呢?

我们已经知道asp.net core 是怎么封装http请求, 并且是通过RequestDelegate委托来处理http请求的。而路由是用来区分不同请求并且将该http请求交由对应的requestDelegate处理。

流程图如下


「Asp.net core」 路由和终结点浅析

重要中间件

路由系统的两个重要的中间件分别是【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要先注册。

EndPoints 结构

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之后,终结点列表就会有一堆终结点,等着用户过来匹配。

所以

  • 在调用UseRouting之前,你可以注册一些用于修改路由操作的数据,比如UseRewriter、UseHttpMethodOverride、UsePathBase等。
  • 在调用UseRouting和UseEndpoints之间,可以注册一些用于提前处理路由结果的中间件,如UseAuthentication、UseAuthorization、UseCors等

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); });

路由的功能远不止这些,如果路由匹配可以用模式匹配,有一些路由模板,会根据模板生成对应的终结点,还有路由约束,路由优先级等等。