WebApi过滤器初学习

WebApi 过滤器

Authorization Filters  //权限验证
Resource Filters   //资源验证   常被用于缓存
Model Binding  //模型绑定
Action Filters  //行为   常被用于模型验证和日志记录
Exception Filters  //异常处理  常被用于错误日志
Result Filters    //结果验证  用于

三种注册方式

  • 方法注册 注册在方法上 Action
  • 类注册 注册在控制器上 Controller
  • 全局注册 注册在Program

过滤器的注解的定义

 public class CtmResourceFilterAttribute : Attribute, IResourceFilter
 {
     public void OnResourceExecuted(ResourceExecutedContext context)
     {
         throw new NotImplementedException();
     }
     //控制器请求前,可以有效减少服务器的压力
     public void OnResourceExecuting(ResourceExecutingContext context)
     {
         //可以添加短路器
         throw new NotImplementedException();
     }
 }
 public class CtmAuthriztionFuilterAttribute : Attribute, IAuthorizationFilter
 {
     //在请求完成前
     public void OnAuthorization(AuthorizationFilterContext context)
     {
         throw new NotImplementedException();
     }
 }
  public class CtmActionFilterAttribute : Attribute, IActionFilter
  {
      //请求结束后
      public void OnActionExecuted(ActionExecutedContext context)
      {
          throw new NotImplementedException();
      }
      //请求结束前
      public void OnActionExecuting(ActionExecutingContext context)
      {
          throw new NotImplementedException();
      }
  }

三种方法权重不一样,执行的依次顺序

progarm->类注册->方法注册

相同类型的FIlter,全局注册的优先级>类上注册优先级>方法上的优先级

不同类型的拦截器互不影响,按照图例顺序

方法注册

namespace FilterDemo.Controllers
{
    [ApiController]
    [Route("[controller]/[action]")]
    public class WeatherForecastController : ControllerBase
    {
        private readonly ILogger<WeatherForecastController> _logger;
        public WeatherForecastController(ILogger<WeatherForecastController> logger)
        {
            _logger = logger;
        }
        [HttpGet]
        [CtmActionFilter]//行为拦截
        [CtmAuthriztionFuilter]//鉴权
        public string Get()
        {
            return "";
        }
    }
}

类注册

namespace FilterDemo.Controllers
{
    [ApiController]
    [Route("[controller]/[action]")]
    [CtmActionFilter]//行为拦截
    [CtmAuthriztionFuilter]//鉴权
    [CtmResourceFilter]//资源拦截
    public class WeatherForecastController : ControllerBase
    {

        private readonly ILogger<WeatherForecastController> _logger;

        public WeatherForecastController(ILogger<WeatherForecastController> logger)
        {
            _logger = logger;
        }

        [HttpGet]
        public string Get()
        {
            return "";
        }

    }
}

Progarm注册

builder.Services.AddControllers(o=>o.Filters.Add<CtmActionFilterAttribute>());
  builder.Services.AddControllers(o=> { 
      //数字越小,越先执行
      o.Filters.Add<CtmActionFilterAttribute>(1);
      o.Filters.Add<CtmActionFilterAttribute>(2);
  });

过滤器的执行顺序

在方法注册上添加order比重

[HttpGet]
// [CtmActionFilter]//行为拦截
[CtmAuthriztionFuilter]//鉴权
[TypeFilter(typeof(CtmResourceFilterAttribute))]//资源拦截
[TypeFilter(typeof(CtmExceptionFilterAttriubute))]//异常处理拦截
[CtmResultFilter]
//在特性中,第一个是作为构造函数传入的,第二个是给属性传参,要把属性的名称填上
[TypeFilter(typeof(CtmActionFilterAttribute),Order =1)]
public string Get()
{
    return "";
}

就算是全局定义的filter,如果自行定义的order没有方法注册的比重大,那么,仍然要先执方法注册的

授权过滤器

AuthorizationFilter

过滤器管道中的第一个过滤器。

控制对操作方法的访问。

这是一个简单的模拟,目的是拿取参数中的值

短路器

前置过滤器有AuthorizationFilter和ResouceFilter,这两个过滤器是指在控制器的构造函数执行之前执行的,这两个过滤器执行后,执行构造函数,在执行Model Binding模型绑定和ActionFilter

ResourceFilter

其中Result正常来说为null,如果不等于null,正常来说,应该是短路器被触发了。

短路器被触发后,就不会去触发控制器的构造函数了,会直接通过短路器的内部逻辑返回请求。

 public class CtmResourceFilterAttribute : Attribute, IResourceFilter
 {
     private static Dictionary<string, object> _dicCache = new Dictionary<string, object>();
     //控制器处理请求后
     public void OnResourceExecuted(ResourceExecutedContext context)
     {
         //获取当前访问的网址
         PathString path = context.HttpContext.Request.Path;
         //将返回的数据存在数组中
         _dicCache[path] = context.Result as ObjectResult;
     }
     //控制器请求前,可以有效减少服务器的压力
     public void OnResourceExecuting(ResourceExecutingContext context)
     {
         //可以添加短路器,这里就触发短路了
         //context.Result = new JsonResult("没有权限");

         //获取当前的访问地址
         var path = context.HttpContext.Request.Path;
         //字典中是否包含path
         if (_dicCache.ContainsKey(path))
         {
             //如果包含,直接返回缓存的数据
             context.Result = _dicCache[path] as ObjectResult;
         }
        
     }
 }

ActionFilter

用于 记录日志,模型认证

以下代码讲的是,如何用ActionFilter去记录日志和特性注入

过滤器实现构造函数注入

[HttpPost]
[TypeFilter(typeof(CtmActionFilterAttribute))]
public User AddUser(User user)
{
    return user;
}
 //一个特性,关于方法的拦截
 public class CtmActionFilterAttribute : Attribute, IActionFilter
 {
     private readonly ILogger logger;

     public CtmActionFilterAttribute(ILogger<CtmActionFilterAttribute> logger)
     {
         this.logger = logger;
     }


     //请求结束后
     public void OnActionExecuted(ActionExecutedContext context)
     {
         throw new NotImplementedException();
     }
     //请求结束前
     public void OnActionExecuting(ActionExecutingContext context)
     {
         logger.LogInformation("....");
     }
 }

还有一种注册方式

 [HttpPost]
 [ServiceFilter(typeof(CtmActionFilterAttribute))]
 public User AddUser(User user)
 {
     return user;
 }

这种方式需要将filter在Progarm中进行依赖注册

builder.Services.AddTransient<CtmActionFilterAttribute>();
//这里注册的是Transient,瞬态

如果使用了ServiceFilter,那么无论是Filter本身还是filter需要注入的参数,都需要在容器中注入。

 builder.Services.AddTransient<User>();
public class CtmActionFilterAttribute : Attribute, IActionFilter
{
    private readonly ILogger logger;
    private readonly User user;

    public CtmActionFilterAttribute(ILogger<CtmActionFilterAttribute> logger,User user)
    {
        this.logger = logger;
        this.user = user;
    }


    //请求结束后
    public void OnActionExecuted(ActionExecutedContext context)
    {
        throw new NotImplementedException();
    }
    //请求结束前
    public void OnActionExecuting(ActionExecutingContext context)
    {
   
        PathString path = context.HttpContext.Request.Path;
        var argumnet = context.ActionArguments;

        if (argumnet.ContainsKey("User"))
        {
            var user = argumnet["user"] as User;
            logger.LogInformation($"{user.Name} is visiting {path}");

        }
        else
        {
            logger.LogInformation($"{user.Name} is visiting {path}");
        }

    }

利用缓存实现短路

缓存使用menory catch

首先要对menory catch进行注册

注意注册时,不能用瞬态,使用单例模式,保持缓存的存在

builder.Services.AddSingleton<IMemoryCache,MemoryCache>();

对控制器使用

        [HttpGet]
        // [CtmActionFilter]//行为拦截
        [CtmAuthriztionFuilter]//鉴权
        [TypeFilter(typeof(CtmResourceFilterAttribute))]//资源拦截
        public string Get()
        {
            return "";
        }

这样才能在ResourceFilterAttribute中拿到相应的缓存

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Caching.Memory;

namespace FilterDemo.Filters
{
    public class CtmResourceFilterAttribute : Attribute, IResourceFilter
    {
        private static Dictionary<string, object> _dicCache = new Dictionary<string, object>();
        private readonly IMemoryCache memoryCache;

        //控制器处理请求后
        public CtmResourceFilterAttribute(IMemoryCache memoryCache)
        {
            this.memoryCache = memoryCache;
        }
        public void OnResourceExecuted(ResourceExecutedContext context)
        {
            /* //获取当前访问的网址
             PathString path = context.HttpContext.Request.Path;
             //将返回的数据存在数组中
             _dicCache[path] = context.Result as ObjectResult;*/


            //获取当前访问的网址
            PathString path = context.HttpContext.Request.Path;
            //不再用字典,用缓存进行存储返回的结果
            //两个参数,一个key一个value
            memoryCache.Set(path, context.Result as ObjectResult); 


        }
        //控制器请求前,可以有效减少服务器的压力
        public void OnResourceExecuting(ResourceExecutingContext context)
        {
            //可以添加短路器,这里就触发短路了
            //context.Result = new JsonResult("没有权限");
            //获取当前的访问地址
            var path = context.HttpContext.Request.Path;
            //字典中是否包含path
           /* if (_dicCache.ContainsKey(path))
            {
                //如果包含,直接返回缓存的数据
                context.Result = _dicCache[path] as ObjectResult;
            }*/
           

            //从缓存中拿到当前path对应的值
            //有值返回true,没有值返回false
            if(memoryCache.TryGetValue(path,out object value))
            {
                context.Result = value as ObjectResult;
            }
        }
    }
}

关于out

  if(memoryCache.TryGetValue(path,out object value))
   {
      context.Result = value as ObjectResult;
   }

解析版

 object value;
 if(memoryCache.TryGetValue(path,out value))
  {
      context.Result = value as ObjectResult;
  }

给参数赋值后,加上out,可以将返回值,和加上out修饰的值一并带出来。

ExceptionFilters

异常处理拦截

作用是捕获异常的处理

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

namespace FilterDemo.Filters
{
    public class CtmExceptionFilterAttriubute : Attribute, IExceptionFilter
    {
        private readonly ILogger<CtmExceptionFilterAttriubute> logger;

        public CtmExceptionFilterAttriubute(ILogger<CtmExceptionFilterAttriubute> logger)
        {
            this.logger = logger;
        }
        //在这里可以拿到错误信息
        public void OnException(ExceptionContext context)
        {
            logger.LogInformation(context.Exception.Message);
            context.Result = new ContentResult
            {
                Content = context.Exception.Message
            };
        }
    }
}

 [HttpGet]
 // [CtmActionFilter]//行为拦截
 [CtmAuthriztionFuilter]//鉴权
 [TypeFilter(typeof(CtmResourceFilterAttribute))]//资源拦截
 [TypeFilter(typeof(CtmExceptionFilterAttriubute))]//异常处理拦截
 public string Get()
 {
     return "";
 }

异常过滤器的捕获范围

可以用于常见的错误处理策略

非常适合捕获发生在操作中的异常

建议使用中间件的处理异常

基于所调用的操作方法,仅当错误处理不同时,才使用异常过滤器。

只能捕获跟action相关的异常,只能捕获操作相关的异常

捕获范围很小

ResultFilters

结果过滤器,围绕着操作结果执行。

只有当操作能够生成结果时,才会使用结果过滤器。

在异常过滤器通过生成操作结果时,结果过滤器不会执行。

结果过滤器中的异常不会被异常过滤器捕获

但是如果继承了IAlwaysRunResultFilter接口,那么就可以在异常过滤器捕获异常后仍然可以进入结果过滤器

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

namespace FilterDemo.Filters
{
    public class CtmAlwaysResultFilter : Attribute, IAlwaysRunResultFilter
    {
        public void OnResultExecuted(ResultExecutedContext context)
        {
            throw new NotImplementedException();
        }

        public void OnResultExecuting(ResultExecutingContext context)
        {
            if (context.Result is StatusCodeResult statusCodeResult
                && statusCodeResult.StatusCode == StatusCodes.Status404NotFound)
            {
                context.Result = new ObjectResult("这是404")
                {
                    StatusCode = StatusCodes.Status404NotFound
                };
            }
        }
    }
}

posted @   想要来杯咖啡吗  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示