asp.net core过滤器应用

筛选器类型

授权筛选器

授权过滤器是过滤器管道的第一个被执行的过滤器,用于系统授权。一般不会编写自定义的授权过滤器,而是配置授权策略或编写自定义授权策略。简单举个例子。

cs 复制代码
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 这个标签的控制器
            // 过滤掉带有AllowAnonymousFilter
            if (HasAllowAnonymous(context))
            {
                return;
            }
            // 获取当前用户的信息
            var user = context.HttpContext.User;

            // 自定义的授权检查逻辑
            if (user == null || user?.Identity?.IsAuthenticated != true)
            {
                // 如果检查不通过,设置 Result 属性为一个 IActionResult 对象,可以阻止请求进一步被处理
                context.Result = new ForbidResult();
            }
        }

        // 判断是否含有IAllowAnonymous
        private 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黑名单和白名单

cs 复制代码
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执行、异常过滤器、结果过滤器以及结果执行。

缓存结果提高网站的响应速度,缓存后,就可以直接从内存中直接取数据,而无需在执行方法。

cs 复制代码
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,而不是异常过滤器。

案例:接口访问日志记录,完整的日志记录方便跟踪分析问题以及攻击

cs 复制代码
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));
        }
    }
}

异常筛选器

监听全局异常并统一格式返回

cs 复制代码
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;
        }
    }
}

结果筛选器

对返回的结果封装,统一结果返回

cs 复制代码
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、只能拦截JsonResult
                CommonResult 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、只能拦截JsonResult
                CommonResult 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页面筛选器
异常过滤器理解
错误处理
过滤器应用

相关推荐
zopple6 分钟前
常见的 Spring 项目目录结构
java·后端·spring
cjy0001112 小时前
springboot的 nacos 配置获取不到导致启动失败及日志不输出问题
java·spring boot·后端
小江的记录本3 小时前
【事务】Spring Framework核心——事务管理:ACID特性、隔离级别、传播行为、@Transactional底层原理、失效场景
java·数据库·分布式·后端·sql·spring·面试
sheji34163 小时前
【开题答辩全过程】以 基于springboot的校园失物招领系统为例,包含答辩的问题和答案
java·spring boot·后端
程序员cxuan3 小时前
人麻了,谁把我 ssh 干没了
人工智能·后端·程序员
wuyikeer4 小时前
Spring Framework 中文官方文档
java·后端·spring
Victor3564 小时前
MongoDB(61)如何避免大文档带来的性能问题?
后端
Victor3565 小时前
MongoDB(62)如何避免锁定问题?
后端
wuyikeer5 小时前
Spring BOOT 启动参数
java·spring boot·后端
子木HAPPY阳VIP6 小时前
Ubuntu 22.04 VMware 设置固定IP配置
人工智能·后端·目标检测·机器学习·目标跟踪