- 概述
最近在尝试做将asp.net webapi项目转移为asp.net core webapi项目的技术试验,今天开始测试认证授权、资源控制、Action与Result控制、以及异常控制的技术变化与请求流程。在asp.net项目中主要采用过滤器(Filter)通过AOP编程模式,拦截客户端发起的请求,然后进行验证,而在asp.net core出现了中间件(Middleware)的概念,且过滤器被归类到mvc命名空间下,因此中间件才是请求管道的正统。
首先说明,本文仅分析中间件和过滤器的执行顺序和过程(实用为先),不会告诉你为什么会出现中间件以及如何使用,如有此需求,请去官网学习。
从微软asp.net core官网教程知晓,asp.net core处理请求时,先执行中间件,后执行过滤器,顺序如下图所示:
- 中间件
- 设计
中间件的定义,它是一装配到应用管道以处理请求和响应的组件。在每一个组件中都可以做两件事:
a)选择是否将请求传递到管道中的下一个组件。
b)可在管道中的下一个组件前后执行工作。
请求委托用于生成请求管道,请求委托处理每一个HTTP请求。
上图展示了中间件完整的请求处理管道顺序,不过我们仅关心Custom middlewares这部分内容,因为我们会忽略asp.net core提供的那套功能,在实际编程中自己实现。
由于在全新的asp.net core框架里一切资源皆依赖注入化(DI),所以中间件也不例外。要使用定义的中间件,首先把中间件添加IServiceCollection集合中,然后再WebApplication对象的UseMiddleware方法启用。特别注意,使用UseMiddleware方法启用中间件时,一定要注意顺序问题,这是它与过滤器不同之处,框架提供的那些过滤器已经定义好了执行顺序,中间件需要你自己排序。
以下内容将从五种中间件的定义和使用展示编码顺序和过程
- 定义
a)AuthMiddleware
作用:权控中间件,用于认证和授权验证。
代码:1 public class AuthMiddleware : IMiddleware 2 { 3 public async Task InvokeAsync(HttpContext context, RequestDelegate next) 4 { 5 Console.WriteLine("auth before"); 6 await next(context); 7 Console.WriteLine("auth after"); 8 } 9 }
b)ResourceMiddleware
作用:资源中间件,用于防盗链等。
代码:1 public class ResourceMiddleware : IMiddleware 2 { 3 public async Task InvokeAsync(HttpContext context, RequestDelegate next) 4 { 5 Console.WriteLine("resource before"); 6 await next(context); 7 Console.WriteLine("resource after"); 8 } 9 }
c)ActionMiddleware
作用:方法中间件,用于拦截每一个请求的Action方法。
代码:1 public class ActionMiddleware : IMiddleware 2 { 3 public async Task InvokeAsync(HttpContext context, RequestDelegate next) 4 { 5 Console.WriteLine("action before"); 6 await next(context); 7 Console.WriteLine("action after"); 8 } 9 }
d)ExceptionMiddleware
作用:异常中间件,进行全局的异常信息收集和处理。
代码:1 public class ExceptionMiddleware : IMiddleware 2 { 3 public async Task InvokeAsync(HttpContext context, RequestDelegate next) 4 { 5 Console.WriteLine("exception before"); 6 await next(context); 7 Console.WriteLine("exception after"); 8 } 9 }
e)ResultMiddleware
作用:结果中间件,可以对结构进行格式化处理、数据转换等操作。
代码:1 public class ResultMiddleware : IMiddleware 2 { 3 public async Task InvokeAsync(HttpContext context, RequestDelegate next) 4 { 5 Console.WriteLine("result before"); 6 await next(context); 7 Console.WriteLine("result after"); 8 } 9 }
- 使用
1 using webapi.test.auth.Functions; 2 using webapi.test.auth.Middlewares; 3 4 var builder = WebApplication.CreateBuilder(args); 5 6 // Add services to the container. 7 builder.Services.AddControllers(); 8 builder.Services.AddSingleton<IGreeter,Greeter>(); 9 builder.Services.AddSingleton<AuthMiddleware>(); 10 builder.Services.AddSingleton<ResourceMiddleware>(); 11 builder.Services.AddSingleton<ActionMiddleware>(); 12 builder.Services.AddSingleton<ExceptionMiddleware>(); 13 builder.Services.AddSingleton<ResultMiddleware>(); 14 15 var app = builder.Build(); 16 // Configure the HTTP request pipeline. 17 app.UseMiddleware<AuthMiddleware>(); 18 app.UseMiddleware<ResourceMiddleware>(); 19 app.UseMiddleware<ActionMiddleware>(); 20 app.UseMiddleware<ExceptionMiddleware>(); 21 app.UseMiddleware<ResultMiddleware>(); 22 23 app.MapControllers(); 24 25 app.Run();
- 设计
- 过滤器
- 设计
通过使用asp.net core中的过滤器,可以在请求处理管道中的特定阶段之前或之后运行功能控制代码。过滤器实际上就是以面向切面编程(AOP)的方式解决编程需求,将大量重复性的代码抽象提取出来,降低业务功能代码的复杂度,使业务功能逻辑编码更加专注,代码结构更加清晰明了。
注意事项:过滤器适用于Razor Pages、API控制器和具有视图的控制器,但是不能直接用于Razor组件。
通常过滤器会和特性一起使用,这会是过滤器的使用更加灵活。过滤器分为同步和异步两种类型,实际编程中任意实现一个即可,而不是同时实现,运行时会先查看过滤器是否实现异步接口,如果是则调用该接口,否则调用同步接口的方法。如果在一个类中同时实现异步和不同接口,则仅调用异步方法。
以下内容将从五种过滤器的定义和使用展示编码顺序和过程 - 定义
a)ApiAuthAttribute
作用:权控过滤器,用于认证和授权验证。
代码:1 [AttributeUsage(AttributeTargets.Class)] 2 public class ApiAuthAttribute : Attribute, IAuthorizationFilter 3 { 4 public void OnAuthorization(AuthorizationFilterContext context) 5 { 6 7 } 8 }
b)ApiResourceAttribute
作用:资源过滤器,用于资源缓存、防盗链等。
代码:1 [AttributeUsage(AttributeTargets.Class)] 2 public class ApiResourceAttribute : Attribute, IResourceFilter 3 { 4 public void OnResourceExecuted(ResourceExecutedContext context) 5 { 6 } 7 8 public void OnResourceExecuting(ResourceExecutingContext context) 9 { 10 11 } 12 }
c)ApiActionAttribute
作用:方法过滤器,用于拦截每一个请求的Action方法。
代码:1 [AttributeUsage(AttributeTargets.Class)] 2 public class ApiActionAttribute : Attribute, IActionFilter 3 { 4 public void OnActionExecuting(ActionExecutingContext context) 5 { 6 7 } 8 public void OnActionExecuted(ActionExecutedContext context) 9 { 10 11 } 12 }
d)ApiExceptionAttribute
作用:异常过滤器,进行全局的异常信息收集和处理。
代码:1 [AttributeUsage(AttributeTargets.Class)] 2 public class ApiExceptionAttribute : Attribute, IExceptionFilter 3 { 4 public void OnException(ExceptionContext context) 5 { 6 7 } 8 }
e)ApiResultAttribute
作用:结果过滤器,可以对结构进行格式化处理、数据转换等操作。
代码:1 [AttributeUsage(AttributeTargets.Class)] 2 public class ApiResultAtrribute : Attribute, IResultFilter 3 { 4 public void OnResultExecuted(ResultExecutedContext context) 5 { 6 7 } 8 9 public void OnResultExecuting(ResultExecutingContext context) 10 { 11 12 } 13 }
- 使用
1 [ApiAuth] 2 [ApiResource] 3 [ApiAction] 4 [ApiException] 5 [ApiResult] 6 [ApiController] 7 [Route("[controller]")] 8 public class WeatherForecastController : ControllerBase 9 { 10 //... 11 }
- 扩展
由于在asp.net core框架里一切资源皆以DI方式创建和管理,而过滤器则是以Attribute特性标注的方式注册,且又是在运行时注册的,所以面临在过滤器中使用DI资源的问题。
如何解决?解决这个问题,需要使用TypeFilter和ServiceFilter特性进行标注注册。
a)两者区别
ServiceFilter需要对自定义的Filter进行注册,TypeFilter不需要注册。
ServiceFilter的Filter生命周期由注册时决定,TypeFilter每次都会创建一个新实例。
b)使用方式
TypeFilter
1 [TypeFilter(typeof(ApiException))] 2 public class Test{ 3 //… 4 }
ServiceFilter
1 [ServiceFilter(typeof(ApiException))] 2 public class Test{ 3 //… 4 } 5 6 public void ConfigureServices(IServiceCollection services){ 7 services.AddSingleton<ApiException>(); 8 }
- 设计
通过使用asp.net core中的过滤器,可以在请求处理管道中的特定阶段之前或之后运行功能控制代码。过滤器实际上就是以面向切面编程(AOP)的方式解决编程需求,将大量重复性的代码抽象提取出来,降低业务功能代码的复杂度,使业务功能逻辑编码更加专注,代码结构更加清晰明了。
- 总结 通过上述内容分析得出结论,在webapi编码过程中,应该以中间件形式实现某些需要的功能,毕竟过滤器与依赖注入搭配的不是很方便,而且过滤器已经被归类于aspnetcore.mvc命名空间下了。