筛选器类型
授权筛选器
授权过滤器是过滤器管道的第一个被执行的过滤器,用于系统授权。一般不会编写自定义的授权过滤器,而是配置授权策略或编写自定义授权策略。简单举个例子。
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.Mvc.Filters;namespace FilterStudy01.Filter
{/// <summary>/// 授权过滤器/// builder.Services.AddMvc(options =>{options.Filters.Add(new WjAuthorizationlFilter());});/// [TypeFilter(typeof(WjAuthorizationlFilter))]可以加在类或者控制器上/// 不登陆的情况下访问/Admin/Index/// </summary>public class WjAuthorizationlFilter : IAuthorizationFilter{public void OnAuthorization(AuthorizationFilterContext context){// 需要排除具有AllowAnymons 这个标签的控制器// 过滤掉带有AllowAnonymousFilterif (HasAllowAnonymous(context)){return;}// 获取当前用户的信息var user = context.HttpContext.User;// 自定义的授权检查逻辑if (user == null || user?.Identity?.IsAuthenticated != true){// 如果检查不通过,设置 Result 属性为一个 IActionResult 对象,可以阻止请求进一步被处理context.Result = new ForbidResult();}}// 判断是否含有IAllowAnonymousprivate bool HasAllowAnonymous(AuthorizationFilterContext context){if (context.Filters.Any(filter => filter is IAllowAnonymousFilter)){return true;}// 终节点:里面包含了路由方法的所有元素信息(特性等信息)var endpoint = context.HttpContext.GetEndpoint();return endpoint?.Metadata.GetMetadata<IAllowAnonymous>() != null;}}
}
https://zhuanlan.zhihu.com/p/677748480
https://blog.csdn.net/qq_41942413/article/details/135163599
https://learn.microsoft.com/zh-cn/aspnet/core/security/authorization/simple?view=aspnetcore-9.0
IP过滤器,不过这个可以放到Action过滤器中,看需求,如果全部限制可以放授权筛选器,也可以简单的做个ip黑名单和白名单
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;namespace FilterStudy01.Filter
{/// <summary>/// 实现ip过滤器/// </summary>public class WjlIpAuthorizationFilter : IAuthorizationFilter{public void OnAuthorization(AuthorizationFilterContext context){var allowIps = new List<string>(){"127.0.0.1"};var requestIp = context?.HttpContext?.Connection?.RemoteIpAddress?.ToString() ?? "";if (!allowIps.Contains(requestIp)){var result = new{Success = false,Msg = "非法请求"};if (context != null){context.Result = new JsonResult(result);}}}}
}
资源筛选器
资源过滤器,在授权过滤器执行后执行,该过滤器包含“之前”和“之后”两个行为,包裹了模型绑定、操作过滤器、Action执行、异常过滤器、结果过滤器以及结果执行。
缓存结果提高网站的响应速度,缓存后,就可以直接从内存中直接取数据,而无需在执行方法。
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;namespace FilterStudy01.Filter
{/// <summary>/// 资源过滤器实现缓存/// IAsyncResourceFilter/// 如果继承Attribute 使用方式如下[WjlResouerceFilter]/// 如何不继承 builder.Services.AddMvc(options =>{options.Filters.Add(new WjlResouerceFilter());});/// </summary>public class WjlResouerceFilterAttribute : Attribute, IResourceFilter{private static readonly Dictionary<string, IActionResult?> dic = new Dictionary<string, IActionResult?>();/// <summary>/// 方法执行之后/// </summary>/// <param name="context"></param>public void OnResourceExecuted(ResourceExecutedContext context){var path = context.HttpContext.Request.Path;dic[path] = context?.Result;}/// <summary>/// 方法执行之前/// </summary>/// <param name="context"></param>public void OnResourceExecuting(ResourceExecutingContext context){var path = context.HttpContext.Request.Path;if (dic.ContainsKey(path)){context.Result = dic[path];}}}
}
操作筛选器
操作过滤器,在模型绑定后执行,该过滤器同样包含“之前”和“之后”两个行为,包裹了Action的执行(不包含Controller的创建)。如果Action执行过程中或后续操作过滤器中抛出异常,首先捕获到异常的是操作过滤器的OnActionExecuted,而不是异常过滤器。
案例:接口访问日志记录,完整的日志记录方便跟踪分析问题以及攻击
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Primitives;
using System.Diagnostics;
using System.Dynamic;
using System.Text;
using System.Text.Json;namespace FilterStudy01.Filter
{/// <summary>/// 操作过滤器/// builder.Services.AddMvc(options =>{options.Filters.Add(new WjlAsyncActionFilter());});/// </summary>public class WjlAsyncActionFilter : IAsyncActionFilter{/// <summary>/// 记录请求日志,方便跟踪以及预警/// 需要开启Buffer/// app.Use(next => new RequestDelegate(async context => {context.Request.EnableBuffering();await next(context);}));/// </summary>/// <param name="context"></param>/// <param name="next"></param>/// <returns></returns>public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next){dynamic model = new ExpandoObject();var httpContext = context.HttpContext;var name = context.ActionDescriptor.DisplayName;var actionName = context.ActionDescriptor.RouteValues["action"] ?? ""; //action名字var controllerName = context.ActionDescriptor.RouteValues["controller"] ?? ""; //controller名字var queryStrings = httpContext.Request.QueryString; //GET请求的后缀var fromString = new StringBuilder();if (httpContext.Request.HasFormContentType){var forms = httpContext.Request?.Form; //Form表单请求if (forms != null){foreach (var item in forms){fromString.Append($"{item.Key}={item.Value}");}}}var ipAddress = httpContext?.Connection?.RemoteIpAddress?.ToString();string body = "";StringValues authHeader = "";if (httpContext != null && httpContext.Request != null){if (httpContext.Request.Body != null){httpContext.Request.Body.Position = 0;//读取请求体var sr = new System.IO.StreamReader(httpContext.Request.Body);body = await sr.ReadToEndAsync(); //请求体内容httpContext.Request.Body.Position = 0;}httpContext.Request.Headers.TryGetValue("Authorization", out authHeader);}//赋值model.Headers = authHeader;model.RequestBody = body;model.IPAddress = ipAddress;model.Result = "";model.FormString = fromString.ToString();model.QueryString = queryStrings;model.Action = actionName;model.ActionClassName = name;model.Controller = controllerName;var stopWatch = Stopwatch.StartNew();stopWatch.Reset();await next();stopWatch.Stop();var customerTime = Math.Round(stopWatch?.Elapsed.TotalMilliseconds ?? 0, 2);//ObjectResult、JsonResult、ViewResult、LocalRedirectResult//RedirectResult、RedirectToActionResult、BadRequestResult、BadRequestObjectResult//OkResult OkObjectResult NoContentResult NotFoundResult ForbiddenResult//ChallengeResult StatusCodeResult ObjectResult FileResult(FileContentResult、FilePathResult、FileStreamResult、VirtualFileResult)//ContentResult EmptyResult ActionResult(基类不能直接用)//上面是全部的类型按照需要自己处理var fileresult = context.Result as FileResult;if (fileresult == null){var result = context.Result as ObjectResult;var resValue = result?.Value;if (result != null && resValue != null){model.Result = JsonSerializer.Serialize(resValue);}}else{model.Result = "文件下载";}Console.WriteLine(JsonSerializer.Serialize(model));}}
}
异常筛选器
监听全局异常并统一格式返回
using FilterStudy01.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Text;namespace FilterStudy01.Filter
{/// <summary>/// 使用/// builder.Services.AddMvc(options =>{options.Filters.Add(new WjlExceptionFilter());});/// 并不是所有的异常都捕获,比如mvc中razor页面报错不能捕获/// 可以捕获Controller创建时(也就是只捕获构造函数中抛出的异常)、模型绑定、Action Filter和Action中抛出的未处理异常/// 其他异常不会捕获,可以使用中间件/// </summary>public class WjlExceptionFilter : IAsyncExceptionFilter{/// <summary>/// 获取异常的详细信息/// </summary>/// <param name="ex"></param>/// <returns></returns>private string GetExceptionDetails(Exception ex){if (ex == null){return string.Empty;}StringBuilder sb = new StringBuilder();sb.Append("异常消息: ");sb.Append(ex.Message);sb.Append("\n");sb.Append("堆栈跟踪: ");sb.Append(ex.StackTrace);// 递归获取内部异常的详细信息 if (ex.InnerException != null){sb.Append(GetExceptionDetails(ex.InnerException));}return sb.ToString();}/// <summary>/// 出现异常时触发/// </summary>/// <param name="context"></param>/// <returns></returns>public async Task OnExceptionAsync(ExceptionContext context){// 如果异常没有被处理则进行处理if (context.ExceptionHandled == false){var httpContext = context.HttpContext;//action名字var actionName = context.ActionDescriptor.RouteValues["action"] ?? "";//controller名字var controllerName =context.ActionDescriptor.RouteValues["controller"] ?? "";var path = httpContext.Request.Path;//这里要替换成日志Console.WriteLine(GetExceptionDetails(context.Exception));CommonResult commonResult = new CommonResult();commonResult.ResultNo = 1;commonResult.ResultData = "server error";context.Result = new JsonResult(commonResult);}// 设置为true,表示异常已经被处理了context.ExceptionHandled = true;}}
}using FilterStudy01.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Text;namespace FilterStudy01.Filter
{/// <summary>/// 局部使用/// [WjlExceptionFilter]/// </summary>public class WjlExceptionFilterAttribute : ExceptionFilterAttribute{/// <summary>/// 获取异常的详细信息/// </summary>/// <param name="ex"></param>/// <returns></returns>private string GetExceptionDetails(Exception ex){if (ex == null){return string.Empty;}StringBuilder sb = new StringBuilder();sb.Append("异常消息: ");sb.Append(ex.Message);sb.Append("\n");sb.Append("堆栈跟踪: ");sb.Append(ex.StackTrace);// 递归获取内部异常的详细信息 if (ex.InnerException != null){sb.Append(GetExceptionDetails(ex.InnerException));}return sb.ToString();}public override async Task OnExceptionAsync(ExceptionContext context){// 如果异常没有被处理则进行处理if (context.ExceptionHandled == false){var httpContext = context.HttpContext;//action名字var actionName = context.ActionDescriptor.RouteValues["action"] ?? "";//controller名字var controllerName =context.ActionDescriptor.RouteValues["controller"] ?? "";var path = httpContext.Request.Path;//这里要替换成日志Console.WriteLine(GetExceptionDetails(context.Exception));CommonResult commonResult = new CommonResult();commonResult.ResultNo = 1;commonResult.ResultData = "服务器开小差了";context.Result = new JsonResult(commonResult);}// 设置为true,表示异常已经被处理了context.ExceptionHandled = true;}}
}
结果筛选器
对返回的结果封装,统一结果返回
using FilterStudy01.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;namespace FilterStudy01.Filter
{/// <summary>/// 全局使用/// </summary>public class WjlResultFilter : IResultFilter{/*使用builder.Services.AddMvc(options =>{options.Filters.Add(new WjlResultFilter());});*/public void OnResultExecuted(ResultExecutedContext context){}public void OnResultExecuting(ResultExecutingContext context){//ObjectResult、JsonResult、ViewResult、LocalRedirectResult//RedirectResult、RedirectToActionResult、BadRequestResult、BadRequestObjectResult//OkResult OkObjectResult NoContentResult NotFoundResult ForbiddenResult//ChallengeResult StatusCodeResult ObjectResult FileResult(FileContentResult、FilePathResult、FileStreamResult、VirtualFileResult)//ContentResult EmptyResult ActionResult(基类不能直接用)var jsonResult = context.Result as JsonResult;if (jsonResult != null && jsonResult.Value != null){//1、只能拦截JsonResultCommonResult commonResult = new CommonResult();commonResult.ResultNo = 0;commonResult.ResultData = jsonResult.Value;jsonResult.Value = commonResult;}}}
}using FilterStudy01.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;namespace FilterStudy01.Filter
{/// <summary>/// 局部使用/// </summary>public class WjlResultFilterAttribute : ResultFilterAttribute{public override void OnResultExecuting(ResultExecutingContext context){var jsonResult = context.Result as JsonResult;if (jsonResult != null && jsonResult.Value != null){//1、只能拦截JsonResultCommonResult commonResult = new CommonResult();commonResult.ResultNo = 0;commonResult.ResultData = jsonResult.Value;jsonResult.Value = commonResult;}}}
}
筛选器接口的同步和异步版本任意实现一个,而不是同时实现 。 运行时会先查看筛选器是否实现了异步接口,如果是,则调用该接口。 如果不是,则调用同步接口的方法。 如果在一个类中同时实现异步和同步接口,则仅调用异步方法。 使用抽象类(如 ActionFilterAttribute)时,将为每种筛选器类型仅重写同步方法或仅重写异步方法。
大佬总结的图示
参考
授权过滤器
https://learn.microsoft.com/zh-cn/aspnet/core/mvc/controllers/filters?view=aspnetcore-8.0
https://blog.csdn.net/sD7O95O/article/details/119223675
Razor页面筛选器
异常过滤器理解
错误处理
过滤器应用