ASP.NET Core 过滤器

SunnyFan大约 6 分钟约 1722 字

ASP.NETopen in new window Core 过滤器

过滤器工作原理

过滤器在 ASP.NETopen in new window Core 操作调用管道内运行。 过滤器管道在 ASP.NETopen in new window Core 选择了要执行的操作之后运行:

图

不同过滤器类型在管道模型中的交互方式

图

过滤器作用域

以Attribute特性应用在Action上。

/// <summary>
/// 创建审批统计分类设置
/// </summary>
/// <param name="createDto"></param>
/// <returns></returns>
[HttpPost]
[Authorize("sys:approval:report:category:add")] //典型以Attribute特性应用在Action上
[LogRecord(LogEnum.CREATE)] //典型以Attribute特性应用在Action上
public async Task<CommonResult> CreateAsync([FromBody] CreateUpdateSysApprovalReportCategoryDto createDto)
{
    ulong id = await _sysApprovalReportCategoryService.CreateAsync(createDto);
    return CommonResult.CreateSuccess(id);
}

以Attribute特性应用在Controller上。

 /// <summary>
/// 待办集成维护管理
/// </summary>
[ApiExplorerSettings(GroupName = "basic")]
[ApiController]
[Route(SystemConfig.RequestTodoPrefix + "third_todo_oam")]
[LoginAuthorizFilter] //典型以Attribute特性应用在Controller上
public class SysThirdTodoOAMController : Controller
{
    
}

以全局形式应用在Controller和Action上

 public void ConfigureServices(IServiceCollection services)
{
 services.AddMvc(option =>
              {
                  option.Filters.Add<LoginAuthorizFilter>();
                  option.Filters.Add<GlobalExceptionFilter>();
                  option.Filters.Add<GlobalActionVerifyFilter>();
              });
}

常用过滤器

Authorization filters 授权过滤器

{
    /// <summary>
    /// 功能权限授权验证
    /// </summary>
    public class LoginAuthorizFilter : Microsoft.AspNetCore.Authorization.AuthorizeAttribute, IAuthorizationFilter
    {
        /// <summary>
        /// 授权验证
        /// </summary>
        /// <param name="context"></param>
        public void OnAuthorization(AuthorizationFilterContext context)
        {
            var controllerActionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;
            if (!(context.ActionDescriptor is ControllerActionDescriptor))
            {
                return;
            }
            //匿名访问,不需要token认证、签名和登录
            var allowanyone = controllerActionDescriptor.ControllerTypeInfo.GetCustomAttributes(typeof(IAllowAnonymous), true).Any()
            || controllerActionDescriptor.MethodInfo.GetCustomAttributes(typeof(IAllowAnonymous), true).Any();

          //自由逻辑
        }
    }
}

Resource filters 资源过滤器

// IResourceFilter
{
    public class CustomResourceFilterAttribute : Attribute, IResourceFilter
    {
        private static Dictionary<string, object> CacheDictionary = new Dictionary<string, object>();
        /// <summary>
        /// 在XX资源之前执行
        /// </summary>
        /// <param name="context"></param>
        public void OnResourceExecuting(ResourceExecutingContext context)
        { 
            //throw new Exception("CustomResourceFilterAttribute异常了。。。。");

            string key = context.HttpContext.Request.Path;
            if (CacheDictionary.ContainsKey(key))
            {
                object oResult = CacheDictionary[key];
                context.Result = (IActionResult)oResult;// context.Result 类似于一个断路器,只要对context.Result赋值,就不再继续往后执行了
            }
            Console.WriteLine("CustomResourceFilterAttribute.OnResourceExecuting");
        }

        /// <summary>
        /// 在XX资源之后执行
        /// </summary>
        /// <param name="context"></param>
        public void OnResourceExecuted(ResourceExecutedContext context)
        {
            string key = context.HttpContext.Request.Path;
            CacheDictionary[key] = context.Result;
            Console.WriteLine("CustomResourceFilterAttribute.OnResourceExecuted");
        }
         
    }
}

// IAsyncResourceFilter
{
    public class CustomAsyncResourceFilterAttribute : Attribute, IAsyncResourceFilter
    {

        private static Dictionary<string, object> CacheDictionary = new Dictionary<string, object>();

        /// <summary>
        /// 在资源执行的时候
        /// 
        /// 扩展缓存:
        /// 1.一组数据保存在内存中
        /// 2.存储数据的标识---存数据---把标识附上去,取数据也可以通过这个标识直接去去
        /// 3.缓存有数据,且标识不变---取到的缓存数据也不变
        /// </summary>
        /// <param name="context"></param>
        /// <param name="next"></param>
        /// <returns></returns>
        public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
        {

            string key = context.HttpContext.Request.Path;
            //在这里就应该检查缓存
            //1.如果有缓存,就应该直接响应给前端了,不再继续往后,如果没有--就执行next.Invoke();
            if (CacheDictionary.ContainsKey(key))
            {
                object oResult = CacheDictionary[key];
                context.Result = (IActionResult)oResult;// context.Result 类似于一个断路器,只要对context.Result赋值,就不再继续往后执行了
            }
            else
            {
                ResourceExecutedContext executedContext = await next.Invoke(); //这里的执行就是去执行控制器的实例+Action方法
                 
                CacheDictionary[key] = executedContext.Result;
            }
        }
    }
}

Action filters 操作过滤器

  • 操作过滤器不应用于 Razor Pages。 Razor Pages 支持 IPageFilter 和 IAsyncPageFilter。
// IActionFilter
{
    public class CustomActionFilterAttribute : Attribute, IActionFilter
    {
        /// <summary>
        /// 在XXXAction执行之前
        /// </summary>
        /// <param name="context"></param>
        public void OnActionExecuting(ActionExecutingContext context)
        {
            //如果在这里就判断一下,如果有匿名---就直接过去了,不让你走Filter
            {
                //  context.ActionDescriptor.EndpointMetadata;  //就是记录了要访问的当前的Action上标记的所有的特性

                if (context.ActionDescriptor.EndpointMetadata.Any(f => f.GetType() == typeof(CustomAllowAnonymousAttribute)))
                {
                    return;//如果标记的有这个特性,就返回了,不继续走后面的F 
                }
                else
                {
                    Console.WriteLine("CustomActionFilterAttribute.OnActionExecuting");
                }

            }
        }

        /// <summary>
        /// 在XXAction执行之后
        /// </summary>
        /// <param name="context"></param>
        public void OnActionExecuted(ActionExecutedContext context)
        {
            if (context.ActionDescriptor.EndpointMetadata.Any(f => f.GetType() == typeof(CustomAllowAnonymousAttribute)))
            {
                return;//如果标记的有这个特性,就返回了,不继续走后面的Filter的内容
            }
            else
            {
                Console.WriteLine("CustomActionFilterAttribute.OnActionExecuted");
            }


        }
    }
}

//IAsyncActionFilter
{
    public class CustomAsyncActionFilterAttribute : Attribute, IAsyncActionFilter
    {

        private ILogger<CustomAsyncActionFilterAttribute> _ILogger = null;
        public CustomAsyncActionFilterAttribute(ILogger<CustomAsyncActionFilterAttribute> logger)
        {
            this._ILogger = logger;
        }


        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            {
                object controllerName = context.HttpContext.Request.RouteValues["controller"];
                object actionName = context.HttpContext.Request.RouteValues["action"];
                _ILogger.LogInformation($"{controllerName}--{actionName} 执行前。。"); 
            }
            await next.Invoke();  //就是去执行Action
            {
                object controllerName = context.HttpContext.Request.RouteValues["controller"];
                object actionName = context.HttpContext.Request.RouteValues["action"];
                _ILogger.LogInformation($"{controllerName}--{actionName} 执行后。。"); 
            }

        }
    }
}

Exception filters 异常过滤器

{
    public class CustomExceptionFilterAttribute : Attribute, IExceptionFilter
    {

        private IModelMetadataProvider _IModelMetadataProvider = null;
         
        public CustomExceptionFilterAttribute(IModelMetadataProvider iModelMetadataProvider)
        {
            this._IModelMetadataProvider = iModelMetadataProvider;
        }

        /// <summary>
        /// 当有异常的时候,就到这儿来
        /// </summary>
        /// <param name="context"></param>
        public void OnException(ExceptionContext context)
        {
            //  context.HttpContext;//Http请求的上下文。。得到HttpContext就可以;想要得到什么,这里都有

            //进入都这里以后,就要在这里处理异常
            //如何处理异常?
            //1.判断异常是否被处理过
            //2.如果没有处理---就处理
            //3.异常处理完毕,就要指定当前异常已经被处理过了

            if (!context.ExceptionHandled)// 如果没有处理-- - 就处理
            {
                //分情况讨论:请求的时候,响应的时候,可能会返回不同数据类型  View  Json
                if (IsAjaxRequest(context.HttpContext.Request)) //判断是否是Ajax请求--JSON
                {
                    //返回一个JSON格式
                    context.Result = new JsonResult(new
                    {
                        Succeess = false,
                        Message = context.Exception.Message
                    });
                }
                else
                {
                    //如果不是Ajax请求---返回一个友好的错误页面,告诉你说异常了,让你联系管理员去处理,而不是直接给黄页

                    ViewResult result = new ViewResult { ViewName = "~/Views/Shared/Error.cshtml" }; 
                    result.ViewData = new ViewDataDictionary(_IModelMetadataProvider, context.ModelState);
                    result.ViewData.Add("Exception", context.Exception); 
                    context.Result = result; //断路器---只要对Result赋值--就不继续往后了; 
                }
            }

            context.ExceptionHandled = true;//异常已经被处理过了

            Console.WriteLine("CustomExceptionFilterAttribute.OnException");
        }


        private bool IsAjaxRequest(HttpRequest request)
        { 
            //HttpWebRequest httpWebRequest = null;
            //httpWebRequest.Headers.Add("X-Requested-With", "XMLHttpRequest");

            string header = request.Headers["X-Requested-With"];
            return "XMLHttpRequest".Equals(header);
        }
    }
}

//上述方法拦截异常,在ExceptionFilter之前的无法被拦截,可以在管道模型处理
#region 无法扑捉到的异常补充
app.UseExceptionHandler(configure =>
{
    configure.Run(async context =>
    {
        var exHeader = context.Features.Get<IExceptionHandlerPathFeature>();
        var ex = exHeader.Error;
        ErrorResult result = new ErrorResult();
        if (ex is ErrorException mycustomex)
        {
            result.errmsg = mycustomex.errmsg;
            result.errcode = mycustomex.errcode;
        }
        else
        {
            result.errmsg = "程序异常,服务端出现异常![异常消息]" + ex.Message;
            result.errcode = 500;
        }
        context.Response.StatusCode = 200;
        context.Response.ContentType = "application/json; charset=utf-8";
        await context.Response.Body.WriteAsync(Encoding.UTF8.GetBytes(result.ToJson()));
    });
});
#endregion

Result filters 结果过滤器

// IAlwaysRunResultFilter
{
    public class CustomAlwaysRunResultFilterAttribute : Attribute, IAlwaysRunResultFilter
    {
        private ILogger<CustomAlwaysRunResultFilterAttribute> _ILogger = null;
        public CustomAlwaysRunResultFilterAttribute(ILogger<CustomAlwaysRunResultFilterAttribute> logger)
        {
            this._ILogger = logger;
        }


        public void OnResultExecuting(ResultExecutingContext context)
        {
            _ILogger.LogInformation($"CustomAlwaysRunResultFilterAttribute-OnResultExecuting ");
        }
          
        public void OnResultExecuted(ResultExecutedContext context)
        {
            _ILogger.LogInformation($"CustomAlwaysRunResultFilterAttribute-OnResultExecuted ");
        }

       
    }
}

//IResultFilter
{
    /// <summary>
    /// 注册全局
    /// </summary>
    public class CustomResultFilterAttribute : Attribute, IResultFilter
    {

        private ILogger<CustomResultFilterAttribute> _ILogger = null;
        public CustomResultFilterAttribute(ILogger<CustomResultFilterAttribute> logger)
        {
            this._ILogger = logger;
        }

        /// <summary>
        /// 在结果执行执行之前
        /// </summary>
        /// <param name="context"></param>
        public void OnResultExecuting(ResultExecutingContext context)
        {
            //ViewResult view = (ViewResult)context.Result;
            //view.ViewData["StudentName"] = "超越自我。。。"; 
            //Console.WriteLine("CustomResultFilterAttribute.OnResultExecuting:这里是视图、、渲染 Befor");

            if (context.Result is JsonResult)  //还有点小问题---大家自己处理一下
            {
                JsonResult jsonresult = (JsonResult)context.Result;
                if (jsonresult.Value is not AjaxResult)
                {
                    context.Result = new JsonResult(new AjaxResult()
                    {
                        Success = true,
                        Message = "OK",
                        Data = context.Result
                    });
                }
            }

        }

        /// <summary>
        /// 在结果执行执行之之后
        /// </summary>
        /// <param name="context"></param>
        public void OnResultExecuted(ResultExecutedContext context)
        {
            Console.WriteLine("CustomResultFilterAttribute.OnResultExecuted:这里是视图、、渲染 After");
        }
    }


    public class AjaxResult
    {
        public bool Success { get; set; }
        public string Message { get; set; }
        public object Data { get; set; }
    }
}

内置Attribute过滤器(抽象)

ActionFilterAttribute

{
    public class CustomAllActionAndResultFilterAttribute:ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            base.OnActionExecuting(context);
        }

        public override void OnActionExecuted(ActionExecutedContext context)
        {
            base.OnActionExecuted(context);
        }

        public override Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            return base.OnActionExecutionAsync(context, next);
        }

        public override void OnResultExecuting(ResultExecutingContext context)
        {
            base.OnResultExecuting(context);
        }

        public override void OnResultExecuted(ResultExecutedContext context)
        {
            base.OnResultExecuted(context);
        }

        public override Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
        {
            return base.OnResultExecutionAsync(context, next);
        }
    }
}

ExceptionFilterAttribute

{
    public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
    {
        public override void OnException(ExceptionContext context)
        {
            base.OnException(context);
        }

        public override Task OnExceptionAsync(ExceptionContext context)
        {
            return base.OnExceptionAsync(context);
        }
    }
}

ResultFilterAttribute

{
    public class CustomResultFilterAttribute : ResultFilterAttribute
    {
        public override void OnResultExecuted(ResultExecutedContext context)
        {
            base.OnResultExecuted(context);
        }

        public override void OnResultExecuting(ResultExecutingContext context)
        {
            base.OnResultExecuting(context);
        }

        public override Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
        {
            return base.OnResultExecutionAsync(context, next);
        }
    }
}        

FormatFilterAttribute

{
    public class CustomFormatFilterAttribute : FormatFilterAttribute
    {
      
    }
}

辅助DI

应用场景:过滤器构造函数依赖其它实例化对象

ServiceFilterAttribute

注:此过滤器需di注入

[ServiceFilter(typeof(SignAuthorizeAttribute))]
public class SysThirdTodoController : Controller
{
}

TypeFilterAttribute

[TypeFilter(typeof(SignAuthorizeAttribute))]
public class SysThirdTodoController : Controller
{
}

IFilterFactory

{
    public class CustomDIFactoryAttribute : Attribute, IFilterFactory
    {

        private Type _Type = null;
        public CustomDIFactoryAttribute(Type type)
        {
            this._Type = type;
        }

        public bool IsReusable => true;

        /// <summary>
        /// CreateInstance: 创建实例
        /// </summary>
        /// <param name="serviceProvider"></param>
        /// <returns></returns>
        public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
        { 
            object oInstacne = serviceProvider.GetService(_Type);
            return (IFilterMetadata)oInstacne;
        }
    }
}