概述
最近在尝试做将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方法启用中间件时,一定要注意顺序问题,这是它与过滤器不同之处,框架提供的那些过滤器已经定义好了执行顺序,中间件需要你自己排序。
以下内容将从五种中间件的定义和使用展示编码顺序和过程
定义
- AuthMiddleware
作用:权控中间件,用于认证和授权验证。
代码:public class AuthMiddleware : IMiddleware { public async Task InvokeAsync(HttpContext context, RequestDelegate next) { Console.WriteLine("auth before"); await next(context); Console.WriteLine("auth after"); } }
- ResourceMiddleware
作用:资源中间件,用于防盗链等。
代码:public class ResourceMiddleware : IMiddleware { public async Task InvokeAsync(HttpContext context, RequestDelegate next) { Console.WriteLine("resource before"); await next(context); Console.WriteLine("resource after"); } }
- ActionMiddleware
作用:方法中间件,用于拦截每一个请求的Action方法。
代码:public class ActionMiddleware : IMiddleware { public async Task InvokeAsync(HttpContext context, RequestDelegate next) { Console.WriteLine("action before"); await next(context); Console.WriteLine("action after"); } }
- ExceptionMiddleware
作用:异常中间件,进行全局的异常信息收集和处理。
代码:public class ExceptionMiddleware : IMiddleware { public async Task InvokeAsync(HttpContext context, RequestDelegate next) { Console.WriteLine("exception before"); await next(context); Console.WriteLine("exception after"); } }
- ResultMiddleware
作用:结果中间件,可以对结构进行格式化处理、数据转换等操作。
代码:public class ResultMiddleware : IMiddleware { public async Task InvokeAsync(HttpContext context, RequestDelegate next) { Console.WriteLine("result before"); await next(context); Console.WriteLine("result after"); } }
使用
using webapi.test.auth.Functions;
using webapi.test.auth.Middlewares;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddSingleton<IGreeter,Greeter>();
builder.Services.AddSingleton<AuthMiddleware>();
builder.Services.AddSingleton<ResourceMiddleware>();
builder.Services.AddSingleton<ActionMiddleware>();
builder.Services.AddSingleton<ExceptionMiddleware>();
builder.Services.AddSingleton<ResultMiddleware>();
var app = builder.Build();
// Configure the HTTP request pipeline.
app.UseMiddleware<AuthMiddleware>();
app.UseMiddleware<ResourceMiddleware>();
app.UseMiddleware<ActionMiddleware>();
app.UseMiddleware<ExceptionMiddleware>();
app.UseMiddleware<ResultMiddleware>();
app.MapControllers();
app.Run();
过滤器
设计
通过使用asp.net core中的过滤器,可以在请求处理管道中的特定阶段之前或之后运行功能控制代码。过滤器实际上就是以面向切面编程(AOP)的方式解决编程需求,将大量重复性的代码抽象提取出来,降低业务功能代码的复杂度,使业务功能逻辑编码更加专注,代码结构更加清晰明了。
注意事项:过滤器适用于Razor Pages、API控制器和具有视图的控制器,但是不能直接用于Razor组件。
通常过滤器会和特性一起使用,这会是过滤器的使用更加灵活。过滤器分为同步和异步两种类型,实际编程中任意实现一个即可,而不是同时实现,运行时会先查看过滤器是否实现异步接口,如果是则调用该接口,否则调用同步接口的方法。如果在一个类中同时实现异步和不同接口,则仅调用异步方法。
以下内容将从五种过滤器的定义和使用展示编码顺序和过程
定义
- ApiAuthAttribute
作用:权控过滤器,用于认证和授权验证。
代码:[AttributeUsage(AttributeTargets.Class)] public class ApiAuthAttribute : Attribute, IAuthorizationFilter { public void OnAuthorization(AuthorizationFilterContext context) { } }
- ApiResourceAttribute
作用:资源过滤器,用于资源缓存、防盗链等。
代码:[AttributeUsage(AttributeTargets.Class)] public class ApiResourceAttribute : Attribute, IResourceFilter { public void OnResourceExecuted(ResourceExecutedContext context) { } public void OnResourceExecuting(ResourceExecutingContext context) { } }
- ApiActionAttribute
作用:方法过滤器,用于拦截每一个请求的Action方法。
代码:[AttributeUsage(AttributeTargets.Class)] public class ApiActionAttribute : Attribute, IActionFilter { public void OnActionExecuting(ActionExecutingContext context) { } public void OnActionExecuted(ActionExecutedContext context) { } }
- ApiExceptionAttribute
作用:异常过滤器,进行全局的异常信息收集和处理。
代码:[AttributeUsage(AttributeTargets.Class)] public class ApiExceptionAttribute : Attribute, IExceptionFilter { public void OnException(ExceptionContext context) { } }
- ApiResultAttribute
作用:结果过滤器,可以对结构进行格式化处理、数据转换等操作。
代码:[AttributeUsage(AttributeTargets.Class)] public class ApiResultAtrribute : Attribute, IResultFilter { public void OnResultExecuted(ResultExecutedContext context) { } public void OnResultExecuting(ResultExecutingContext context) { } }
使用
[ApiAuth]
[ApiResource]
[ApiAction]
[ApiException]
[ApiResult]
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
//...
}
扩展
由于在asp.net core框架里一切资源皆以DI方式创建和管理,而过滤器则是以Attribute特性标注的方式注册,且又是在运行时注册的,所以面临在过滤器中使用DI资源的问题。
如何解决?解决这个问题,需要使用TypeFilter和ServiceFilter特性进行标注注册。
a)两者区别
ServiceFilter需要对自定义的Filter进行注册,TypeFilter不需要注册。
ServiceFilter的Filter生命周期由注册时决定,TypeFilter每次都会创建一个新实例。
b)使用方式
TypeFilter
[TypeFilter(typeof(ApiException))]
public class Test{
//…
}
ServiceFilter
[ServiceFilter(typeof(ApiException))]
public class Test{
//…
}
public void ConfigureServices(IServiceCollection services){
services.AddSingleton<ApiException>();
}
总结
通过上述内容分析得出结论,在webapi编码过程中,应该以中间件形式实现某些需要的功能,毕竟过滤器与依赖注入搭配的不是很方便,Minimal API不支持过滤器,而且过滤器已经被归类于aspnetcore.mvc命名空间下了。
测试代码:https://gitee.com/kinbor/jks.core.test.middlewareandfilter