Skip to main content

路由

分类:  Asp.net Core入门 标签:  #Asp.Net core基础 #基础 #Web 发布于: 2023-06-04 20:37:05

关于路由这个部分,我想有几个技术要点是需要先理解清楚的,否则的话,可能很多新手同学对于理解Asp.net Core 3.0之后的以中间件: UseRoutingUseEndpoint这个两个概念可能会非常混淆,可能非常不明白为什么既然有了路由的中间件,为什么还需要Endpoint这样一个中间件。

先说为什么?我理解最主要的就是为了解耦,将路由匹配,路径选择这些功能和选择好路由之后实际执行完全分开,也就是如果你愿意,你完全可以自己重新定义路由组件,但是不影响Endpoint中间件的执行。

另外要理解的是我们在StartupConfigure方法中执行的扩展方法,都是在注册将来要在一个request里执行的委托,也就是在该方法里前后执行的顺序并不影响将来在request里执行的顺序,为什么要理解这个呢?这是因为我们发现我们使用了UseRoute注册了路由中间件之后,并没有对Endpoint进行匹配,实际的匹配反而是在UseEndpoint中通过Endpoint的扩展方法来匹配的。

第三点重要的理解,实际上路由就是根据路径和header来进行路由的,这个其实是不变的基本原理。

第四点,就是Endpoint本身概念的理解,我们在Endpoint上实际已经赋予了很多概念,例如有赋予利用一些数据进行定义的规则。例如认证等等。

第五点我们需要注意一个请求在所有的这些中间件进行流动的时候,实际上是有一个对象来表示这个请求的,这就是HttpContext

下面我们来快速的做一下学习记录。

什么是Endpoint

Endpoint是一个抽象的可执行的单位,也就是除了要给出一个怎么和路由匹配的规则,同时还要给出一个匹配了这个规则之后的可以执行的RequestDelte, 关于这个委托,我们可以快速的看一下定义:

public delegate System.Threading.Tasks.Task RequestDelegate(HttpContext context);

可以看到这个委托里唯一个参数就是封装好的http上下文。

同时我们在Endpoint可以使用很多种方法来匹配路由规则,例如:

  • MapGet
  • MapPost
  • Map
  • MapDelete
  • MapPut
  • MapMethod

等等,看看这些方法的定义,基本都是扩展方法,例如:

public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapGet (this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string pattern, Microsoft.AspNetCore.Http.RequestDelegate requestDelegate);

就可以看到这个方法是IEndpointRouteBuilder的扩展方法,然后是一个字符串定义的模式,最后是一个可以执行的委托,使用方法如下:

app.UseEndpoints(endpoints =>
{
    endpoints.MapGet("/hello/{name:alpha}", async context =>
    {
        var name = context.Request.RouteValues["name"];
        await context.Response.WriteAsync($"Hello {name}!");
    });
});

实际上一个endpoint定义了:

  • 一个用来执行的委托。
  • metadata的集合。MetaData非常有用处,及其适合用于切面编程。例如在userouting和useendpoint中间添加其他的处理方式,

可以以下面这个例子理解一下:

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

        // Location 1: Before routing runs. Can influence request before routing runs.
        app.UseHttpMethodOverride();

        app.UseRouting();

        // Location 2: After routing runs. Middleware can match based on metadata.
        app.Use(next => context =>
        {
            var endpoint = context.GetEndpoint();
            if (endpoint?.Metadata.GetMetadata<AuditPolicyAttribute>()?.NeedsAudit
                                                                            == true)
            {
                Console.WriteLine($"ACCESS TO SENSITIVE DATA AT: {DateTime.UtcNow}");
            }

            return next(context);
        });

        app.UseEndpoints(endpoints =>
        {         
            endpoints.MapGet("/", async context =>
            {
                await context.Response.WriteAsync("Hello world!");
            });

            // Using metadata to configure the audit policy.
            endpoints.MapGet("/sensitive", async context =>
            {
                await context.Response.WriteAsync("sensitive data");
            })
            .WithMetadata(new AuditPolicyAttribute(needsAudit: true));
        });

    } 
}

public class AuditPolicyAttribute : Attribute
{
    public AuditPolicyAttribute(bool needsAudit)
    {
        NeedsAudit = needsAudit;
    }

    public bool NeedsAudit { get; }
}

可以从这个例子里看到中间件,以及routting和endpoint之间是怎么交互的。

更为详细的内容,我们后面在学习Razor pageMVC的时候再来学习。

请认真理解一下者两个中间件的设计。