.net core 后端框架 实战2-http请求管道 中间件、筛选器与属性 搭建

在上一篇文章中,我们完成了 .NET Web 框架的基本组成和结构,在这一篇中我们重点来描述使用中间件、过滤器以及属性来构建HTTP 管道,并处理各类请求。

[NET后端框架1-Web后端框架的组成](https://blog.youkuaiyun.com/weixin_55693845/article/details/146315855?

基本概念

ASP.NET Core应用的HTTP的管道搭建
​服务器(Server)​:作为管道入口,负责请求监听与响应输出(如Kestrel服务器默认监听5000端口)

​中间件链(Middleware Chain)​:由多个中间件按注册顺序组成处理链,每个中间件通过Func<RequestDelegate, RequestDelegate>委托实现逻辑叠加

​框架集成层:通过Startup.Configure方法配置中间件,并衔接MVC/Web API等上层框架。

中间件:ASP.NET Core请求处理管道的全局组件、核心组件,通过Startup.Configure注册的无状态处理单元,贯穿整个HTTP请求生命周期(如身份验证、请求日志)。它独立于 MVC 框架,直接处理从客户端发来的请求,并能决定是否将请求传递给下一个中间件,最终生成响应返回客户端
筛选器:MVC框架内的AOP实现机制,在路由匹配后发挥作用。以控制器/方法为作用域,用于对特定的控制器或方法执行前后进行干预,实现如参数验证、授权检查等功能。
属性:属性是一种元数据标记(Metadata),标记于方法、控制器。以声明的方式将筛选器绑定到特定代码单元(如[Authorize]标记控制器访问权限),是筛选器的主要实现手段之一。属性本身不是处理逻辑,需要与筛选器机制配合使用。

一、中间件、筛选器、属性

HTTP请求处理体系
中间件
筛选器
控制器
Action方法
属性
全局管道
MVC框架内
特性声明

属性通过特性标记(如[Route])与控制器/方法形成绑定关系,实现筛选器的局部作用域控制


二、请求处理流程

前端 Middleware Routing Filters Controller 请求进入 认证/日志等 loop [中间件处理- ] 路由匹配 进入MVC框架 授权/验证等 loop [筛选器处理- ] 执行Action 返回响应 前端 Middleware Routing Filters Controller

三、作用域区分

(一)中间件作用域

中间件作用域HTTP管道(跨MVC),使用代码配置。

HTTP请求
异常处理中间件
静态文件中间件
路由中间件
认证中间件
响应压缩中间件

作用域特征:
1. 全流程覆盖:从请求接收到响应返回全程参与
2. 依赖执行顺序:注册顺序决定处理层级(比如异常中间件需先注册)
3. 管道短路能力:可中断请求传递直接响应(比如JWT验证失败返回401等)


(二)筛选器作用域

筛选器作用于MVC框架内,可以注册或者特性的方式绑定。

全局筛选器
控制器层级
Action方法层级
授权筛选器
资源筛选器
Action筛选器

作用域控制策略:
1. 全局注册:通过services.AddMvc(options => options.Filters.Add())影响所有控制器
2. 控制器级:通过类特性标记(如[TypeFilter(typeof(CustomFilter))]
3. 方法级:通过Action特性标记(如[ServiceFilter(typeof(LogFilter))]

四、代码实战:定义、注册与使用

(一)中间件

  1. 定义 :创建一个继承自 IMiddleware 接口的类,在其中实现 InvokeAsync 方法,编写中间件的处理逻辑。例如,以下是一个 Token 校验中间件的定义:

    •  /// <summary>
       /// Token校验中间件
       /// </summary>
       public class TokenMiddleWare : IMiddleware
       {
      
           /// <summary>
           /// token服务
           /// </summary>
           TokenService _tokenService;
      
           /// <summary>
           /// 构造函数,注入tokenservice
           /// </summary>
           /// <param name="tokenService"></param>
           public TokenMiddleWare(TokenService tokenService) 
           {
               _tokenService = tokenService; 
           }
      
           /// <summary>
           /// 中间件调用
           /// </summary>
           /// <param name="context"></param>
           /// <param name="next"></param>
           /// <returns></returns>
           public async Task InvokeAsync(HttpContext context, RequestDelegate next)
           {
               try
               {
                   //前逻辑处理
                   // 获取当前请求的Endpoint
                   var endpoint = context.GetEndpoint();
                   var skiptoken= endpoint?.Metadata.OfType<SkipTokenAttribute>().Any();
                   if (skiptoken == true)  //未标记禁用token检查
                   {
                       if (!_tokenService.CheckToken(context))  //校验token有效性,token从header中解析获取
                       {
                           _ = Res401Async(context);  //校验失败,返回401错误
                           return;
                       }
                   }
      
                   //进入下一个中间件
                   await next(context);
                   //后逻辑处理
               }
               catch (Exception ex)
               {
                   _ = Res500Async(context);  //处理异常,返回服务器500错误
               }
      
           }
      
           /// <summary>
           /// 登录后返回401
           /// </summary>
           /// <param name="context"></param>
           /// <returns></returns>
           public async Task Res401Async(HttpContext context)
           {
               context.Response.StatusCode = 401;
               //
               context.Response?.Headers?.Add("Content-Type", "application/json; charset=utf-8");
               var rspResult = ResultCode.Fail.JsonR("提供的令牌无效或已过期");
               await context.Response.WriteAsync(rspResult.ToJson());
           }
      
           /// <summary>
           /// 登录后返回401
           /// </summary>
           /// <param name="context"></param>
           /// <returns></returns>
           public async Task Res500Async(HttpContext context)
           {
               context.Response.StatusCode = 500;
               //
               context.Response?.Headers?.Add("Content-Type", "application/json; charset=utf-8");
               var rspResult = ResultCode.Fail.JsonR("服务器内部错误");
               await context.Response.WriteAsync(rspResult.ToJson());
           }
       }
      
  2. 注册 :在程序的配置文件中,通过 app.UseMiddleware<T>() 方法注册中间件,注册顺序决定了中间件在管道中的执行顺序。例如:

    •  //自定义中间件,所有请求都使用此中间件
       app.UseMiddleware<TokenMiddleWare>();
      
    • 也可以针对特定路径注册中间件:
    •  //自定义中间件,以/WeatherForecast开头的请求使用此中间件
       app.Map("/WeatherForecast", productapp =>
       {
           productapp.UseMiddleware<TokenMiddleWare>();
       });
      

(二)过滤器

  1. 定义 :创建一个实现相应过滤器接口(如 IActionFilter)的类,在其中编写过滤逻辑。例如,以下是一个记录方法执行时间的过滤器定义:

    •  /// <summary>
       /// 方法执行筛选器(同步)
       /// </summary>
       public class LoggingActionFilter : IActionFilter
       {
           ILogger<LoggingActionFilter> _logger;
      
           /// <summary>
           /// 
           /// </summary>
           /// <param name="logger"></param>
           public LoggingActionFilter(ILogger<LoggingActionFilter> logger)
           {
               _logger = logger;
           }
      
           /// <summary>
           /// 记录方法开始执行时间
           /// </summary>
           /// <param name="context"></param>
           public void OnActionExecuting(ActionExecutingContext context)
           {
               _logger.LogInformation($"Action '{context.ActionDescriptor.DisplayName}' is executing.");
               return;
           }
      
           /// <summary>
           /// 记录方法执行完成时间
           /// </summary>
           /// <param name="context"></param>
           public void OnActionExecuted(ActionExecutedContext context)
           {
               _logger.LogInformation($"Action '{context.ActionDescriptor.DisplayName}' executed.");
               return;
           }
       }
      
  2. 注册 :通过服务容器注册过滤器,例如在 AddControllers 方法中添加过滤器:

    •  //注册 MVC 控制器相关的服务,包括模型绑定、验证、格式化等功能。它还会隐式注册路由服务。
       builder.Services.AddControllers(options =>
       {
           options.Filters.Add<LoggingActionFilter>(); //添加过滤器(筛选器)
       })
      

(三)属性

  1. 定义 :使用 Attribute 类创建自定义属性,通过 AttributeUsage 特性指定属性的适用范围。例如,以下是一个禁用 Token 检查的属性定义:

    •  /// <summary>
       /// 禁用token校验属性过滤器
       /// </summary>
       [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
       public class SkipTokenAttribute: Attribute
       {
       }
      
  2. 标记 :在控制器或方法上使用定义好的属性,以实现特定的功能。例如:

    •  [HttpGet(Name = "GetWeatherForecast")]
       [SkipToken]
       public IEnumerable<WeatherForecast> Get()
       {
           //
           return Enumerable.Range(1, 5).Select(index => new WeatherForecast
           {
               Date = DateTime.Now.AddDays(index),
               TemperatureC = Random.Shared.Next(-20, 55),
               Summary = Summaries[Random.Shared.Next(Summaries.Length)]
           })
           .ToArray();
       }
      
  3. 使用 :在中间件或其他需要的地方,通过反射等方式检测方法是否标记了特定属性,从而决定是否执行相应逻辑。例如,在 Token 中间件中检测是否标记了 SkipTokenAttribute 属性:

    •  /// <summary>
       /// 中间件调用
       /// </summary>
       /// <param name="context"></param>
       /// <param name="next"></param>
       /// <returns></returns>
       public async Task InvokeAsync(HttpContext context, RequestDelegate next)
       {
           try
           {
               //前逻辑处理
               // 获取当前请求的Endpoint
               var endpoint = context.GetEndpoint();
               var skiptoken= endpoint?.Metadata.OfType<SkipTokenAttribute>().Any();
               if (skiptoken == true)  //未标记禁用token检查
               {
                   if (!_tokenService.CheckToken(context))  //校验token有效性,token从header中解析获取
                   {
                       _ = Res401Async(context);  //校验失败,返回401错误
                       return;
                   }
               }
      
               //进入下一个中间件
               await next(context);
               //后逻辑处理
           }
           catch (Exception ex)
           {
               _ = Res500Async(context);  //处理异常,返回服务器500错误
           }
      
       }
      

总结

在 .NET Web 框架中,中间件适用于全局处理逻辑,过滤器聚焦于控制器或方法层面,而属性则为代码实体提供声明式的行为定制手段。在实际开发中,合理搭配使用这三者,能够打造出高性能、高可维护性的 Web 应用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值