gpt4 book ai didi

asp.netcore之中间件

转载 作者:我是一只小鸟 更新时间:2023-07-26 22:31:42 26 4
gpt4 key购买 nike

中间件介绍

在asp.net core中,中间件中间件是一种装配到应用管道以处理请求和响应的软件。 每个组件:

  • 选择是否将请求传递到管道中的下一个组件。
  • 可在管道中的下一个组件前后执行工作。

请求委托用于生成请求管道。 请求委托处理每个 HTTP 请求。 ASP.NET Core 请求管道包含一系列请求委托,依次调用。每个委托均可在下一个委托前后执行操作。 应尽早在管道中调用异常处理委托,这样它们就能捕获在管道的后期阶段发生的异常。 如下图所示:

编写中间件

在asp.net core中已经内置了挺多的中间件,包括身份验证,授权等等,详细的可以看官方文档 内置中间件 列表。 接下来主要讲一下如何编写我们自己的中间件,在前面的文章中我们也用到了自己写的中间件,用的是最简单的app.Use的方式。 Use 扩展可以使用两个重载:

  • 一个重载采用 HttpContext 和 Func 。 不使用任何参数调用 Func
  • 另一个重载采用 HttpContext 和 RequestDelegate。 通过传递 HttpContext 调用 RequestDelegate。

优先使用后面的重载,因为它省去了使用其他重载时所需的两个内部每请求分配.

                        
                          app.Use(async (context, next) =>
{
    // 下游中间件执行前
    await next.Invoke(); //往下执行中间件
    // 下游中间件执行后
});

                        
                      

上面写法就是一个最简单的没有任何操作的中间件。 在调用await next.Invoke()前我们写的操作就是在下游中间件执行之前做的事情,对应的,在之后写的操作则是在下游中间件响应后做的事情。 举个例子,当我们要在下游中间件执行之前,做一些参数的赋值,如我想在Headers中添加一个头部, 。

                        
                          app.Use(async (context, next) =>
{
    context.Request.Headers.Add("TestMiddlewareAdd", "Abc");
    await next.Invoke();
});

                        
                      

在添加之后,下游就可以获取Headers中TestMiddlewareAdd的值。 我们来实操一下,创建一个WebApi项目,然后在Program中MapControllers()之前添加上述中间件。 可以看到,Headers中已经加上了我们之前加的内容。 对应的,如果写在await next.Invoke()后面,则是不生效的,这个可以自行测试。那么在await next.Invoke()后面我们可以做一些什么操作呢?比如记录请求响应完成后的内容,或对相应内容做进一步的处理等等,根据我们的实际需要去写.

除了app.Use(),在asp.net core中还有几种中间件的编写方式.

                        
                          app.Map();
app.MpaWhen();
app.Run();
app.UseMiddleware();

                        
                      

Map扩展用作约定来创建管道分支。 Map 基于给定请求路径的匹配项来创建请求管道分支。 如果请求路径以给定路径开头,则执行分支。 MapWhen基于给定谓词的结果创建请求管道分支。 Func<HttpContext, bool> 类型的任何谓词均可用于将请求映射到管道的新分支。 Run 委托不会收到 next 参数。 第一个 Run 委托始终为终端,用于终止管道。 Run 是一种约定。 某些中间件组件可能会公开在管道末尾运行的 Run[Middleware] 方法:

UseMiddleware

UseMiddleware是我们最常用的封装中间件的方式,中间件类是基于约定编写的。其约定如下:

  • 具有类型为 RequestDelegate 的参数的公共构造函数。
  • 名为 Invoke 或 InvokeAsync 的公共方法。 此方法必须:
    • 返回 Task。
    • 接受类型 HttpContext 的第一个参数。

构造函数和 Invoke/InvokeAsync 的其他参数由依赖关系注入 (DI) 填充.

接下来我们来实操基于约定编写一个Middleware类 。

                        
                              public class AMiddleware
    {
        private readonly RequestDelegate _next;

        public AMiddleware(RequestDelegate next)
            => _next = next;

        public async Task InvokeAsync(HttpContext context, ILogger<AMiddleware> logger)
        {
            logger.LogInformation("AMiddleware Invoke");

            await _next(context);
        }
    }

                        
                      

在Program使用UseMiddleware把中间件加入管道 。

                        
                          app.UseAuthorization();
app.Use(async (context, next) =>
{
    context.Request.Headers.Add("TestMiddlewareAdd", "Abc");
    await next.Invoke();
});
app.UseMiddleware<AMiddleware>();
app.MapControllers();

app.Run();

                        
                      

启动项目发出请求。可以看到下图结果: 需要注意的是,这里的Middleware会自动注册为一个单例,所以在构造器注入时,无法注入Scope生命周期的服务。 如果注入,启动会直接报错 。

                        
                          public class AMiddleware
{
    private readonly RequestDelegate _next;
    private readonly TestMiddlewareDi _testMiddlewareDi;

    public AMiddleware(RequestDelegate next, TestMiddlewareDi testMiddlewareDi)
    {
        _next = next;
        _testMiddlewareDi = testMiddlewareDi;
    }

    public async Task InvokeAsync(HttpContext context, ILogger<AMiddleware> logger)
    {
        logger.LogInformation("AMiddleware Invoke");
        logger.LogInformation($"AMiddleware _testMiddlewareDi: {_testMiddlewareDi.Id}");

        await _next(context);
    }
}

                        
                      
                        
                          builder.Services.AddScoped<TestMiddlewareDi>();

                        
                      

当我们需要注入Scope生命周期的服务时,直接在InvokeAsync方法中添加注入.

                        
                          public class AMiddleware
{
    private readonly RequestDelegate _next;

    public AMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context, ILogger<AMiddleware> logger, TestMiddlewareDi testMiddleware)
    {
        logger.LogInformation("AMiddleware Invoke");
        logger.LogInformation($"AMiddleware _testMiddlewareDi: {testMiddleware.Id}");

        await _next(context);
    }
}

                        
                      

运行结果可以看到,正常运行,并且每次请求Id都是不一样的.

IMiddleware

除了基于约定实现中间件,asp.net core还有一个基于工厂的中间件激活扩展。 IMiddlewareFactory/IMiddleware 是中间件激活的扩展点,具有以下优势:

  • 按客户端请求(作用域服务的注入)激活
  • 让中间件强类型化

UseMiddleware 扩展方法检查中间件的已注册类型是否实现 IMiddleware。 如果是,则使用在容器中注册的 IMiddlewareFactory 实例来解析 IMiddleware 实现,而不使用基于约定的中间件激活逻辑。 中间件在应用的服务容器中注册为作用域或瞬态服务。 接下来我们来实现一个IMiddleware 。

                        
                          public class FactoryMiddleware : IMiddleware
{
    private readonly ILogger _logger;
    private readonly TestMiddlewareDi _testMiddleware;

    public FactoryMiddleware(ILogger<FactoryMiddleware> logger, TestMiddlewareDi testMiddleware)
    {
        _logger = logger;
        _testMiddleware = testMiddleware;
    }

    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        _logger.LogInformation("FactoryMiddleware Invoke");
        _logger.LogInformation($"FactoryMiddleware _testMiddlewareDi: {_testMiddleware.Id}");
        await next(context);
    }
}

                        
                      
                        
                          app.UseAuthorization();
app.Use(async (context, next) =>
{
    context.Request.Headers.Add("TestMiddlewareAdd", "Abc");
    await next.Invoke();
});
app.UseMiddleware<AMiddleware>();
app.UseMiddleware<FactoryMiddleware>();
app.MapControllers();

app.Run();

                        
                      

需要注意的是,这种方式必须把中间件注册到依赖注入容器中,否则会出现以下错误: 注册注入之后,我们再次启动服务,并测试请求.

                        
                          builder.Services.AddScoped<FactoryMiddleware>();

                        
                      

一切顺利执行.

基于约定的中间件和基于工厂的中间件区别

基于约定的中间件无法通过构造函数注入Scope生命周期的服务,只能通过Invoke方法的参数进行注入。 基于工厂的中间件只能通过构造函数添加注入,Invoke无法注入(因为是基于IMiddleware接口的实现).

基于约定的中间件无需手动注册进依赖注入容器。 基于工厂的中间件必须注册进依赖注入容器,且生命周期注册为作用域或瞬态服务.

基于约定的中间件生命周期为单例 基于工厂的中间件生命周期为作用域 。

中间件顺序

中间既然是一种管道的模式,那么必然和顺序有关系,管道前面的中间件先执行,后面的中间件后执行。 那么这个顺序会带来哪种影响呢? 这里盗官方文档图,下图显示了 ASP.NET Core MVC 和 Razor Pages 应用的完整请求处理管道。 这里UseCors 和 UseStaticFiles 顺序是最容易看出影响的。 若是UseStaticFiles在UseCors之前调用,则检索静态文件时,不会检查是否跨站点调用。所有静态文件可以直接检索。 若是相反,则在跨站检索静态文件时,则会优先检查站点是否跨域,若是跨域则无法检索静态文件.

由此我们可以想到,当我们需要做一些前置校验的中间件时,可以把中间件顺序放在前面,校验不通过直接终止后续请求,可以提高应用的响应效率.

欢迎进群催更.

最后此篇关于asp.netcore之中间件的文章就讲到这里了,如果你想了解更多关于asp.netcore之中间件的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com