Bootstrap

asp.net core过滤器应用

筛选器类型

授权筛选器

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

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黑名单和白名单

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

;