zoukankan      html  css  js  c++  java
  • 【aspnetcore】在过滤器(Filter)中使用注入服务(ServiceFilter|TypeFilter)

    在MVC中,AOP是很常用的功能,我们经常会使用如 ActionFilter,IAuthorizeFilter 等描述对Controller和Action进行约束和扩展,一般做法如下:

    public class TestActionFilterAttribute : Attribute, IActionFilter
    {
    
        public void OnActionExecuted(ActionExecutedContext context)
        {
            if (context.HttpContext.Request.Query.TryGetValue("id", out StringValues value))
            {
                Console.WriteLine(value.First());
            }
            else
            {
                context.HttpContext.Response.Redirect("/Error/404");
            }
        }
    
        public void OnActionExecuting(ActionExecutingContext context)
        { }
    }
    

    上面的代码很简单,就是判断请求中是否包含id参数,如果有,则打印id;如果没有,则跳转到错误页面。用法也很简单,在需要约束的Action上添加[TestActionFilter]即可。

    [TestActionFilter]        
    public IActionResult Index()
    {
        return View();
    }

    这是Filter最基本的用法,但是,如果我们需要在Filter中使用注入的服务怎么办?比如说修改下 TestActionFilterAttribute:

    public class TestActionFilterAttribute : Attribute, IActionFilter
    {
        private readonly ILogger _logger;
    
        public TestActionFilterAttribute(ILoggerFactory logger)
        {
            _logger = logger.CreateLogger("TestActionFilterAttribute");
        }
    
        public void OnActionExecuted(ActionExecutedContext context)
        {
            var path = context.HttpContext.Request.Path;
            _logger.LogDebug($"{path} 开始运行了");
        }
    
        public void OnActionExecuting(ActionExecutingContext context)
        { }
    }
    

    我们在Filter的构造函数中注入ILoggerFactory参数,这是系统默认提供的日志工厂服务,用来在控制台打印消息。

    回到Controller文件,发现[TestActionFilter]报错:未提供与"TestActionFilterAttribute"的必需形参logger对应的实参。好吧,下面我们尝试构造一个logger对象

    public class HomeController : Controller
    {
        private readonly ILoggerFactory _loggerFactory;
    
        public HomeController(ILoggerFactory factory)
        {
            _loggerFactory = factory;
        }
    
        [TestActionFilter(_loggerFactory)]
        public IActionResult Index()
        {
            return View();
        }
    }
    

    修改过后,继续报错:特性构造函数参数"logger"具有类型ILoggerFactory,这不是有效特性参数类型。由此可见,如果在Filter中需要注入服务,常规的方式是无法实现的。

    如果一定需要调用注入服务该怎么实现呢?其实框架已经为我们提供了两种途径:TypeFilter和ServiceFilter

    public class TestTypeFilter : IActionFilter
    {
        private readonly ILogger _logger;
    
        public TestTypeFilter(ILoggerFactory logger)
        {
            _logger = logger.CreateLogger("TestTypeFilter");
        }
    
        public void OnActionExecuted(ActionExecutedContext context)
        {
            var path = context.HttpContext.Request.Path;
            _logger.LogDebug($"{path} 开始运行了");
        }
    
        public void OnActionExecuting(ActionExecutingContext context)
        { }
    }
    

    这里的代码和上面修改过的TestActionFilterAttribute一模一样,修改下Controller文件:

    [TypeFilter(typeof(TestTypeFilter))]
    public IActionResult Index()
    {
        return View();
    }
    

    运行测试,效果如下:

     可以看到,代码运行正常。

    下面再看看ServiceFilter的用法,新建文件 TestServiceFilter

    public class TestServiceFilter : IActionFilter
    {
        private readonly ILogger _logger;
    
        public TestServiceFilter(ILoggerFactory logger)
        {
            _logger = logger.CreateLogger("TestServiceFilter");
        }
    
        public void OnActionExecuted(ActionExecutedContext context)
        {
            var path = context.HttpContext.Request.Path;
            _logger.LogDebug($"{path} 开始运行了");
        }
    
        public void OnActionExecuting(ActionExecutingContext context)
        { }
    }
    

    修改Controller文件:

    //[TypeFilter(typeof(TestTypeFilter))]
    [ServiceFilter(typeof(TestServiceFilter))]
    public IActionResult Index()
    {
           return View();
    }
    

    仅仅这样是不够的,顾名思义,ServiceFilter(服务过滤器),我们需要到startup.cs的ConfiguraionServices中注册TestServiceFilter:

    services.AddSingleton<TestServiceFilter>();
    

    运行测试,效果如下:

    OK,运行正常!

    下面是补充内容,添加一个全局异常过滤器:

    新建文件 MvcGlobalExceptionFilter.cs

    public class MvcGlobalExceptionFilter : IExceptionFilter
    {
        private readonly ILogger _logger;
    
        public MvcGlobalExceptionFilter(ILoggerFactory logger)
        {
            _logger = logger.CreateLogger("MvcGlobalExceptionFilter");
        }
    
        public void OnException(ExceptionContext context)
        {
            // 全局异常的错误处理
            _logger.LogError(context.Exception, "全局异常");
        }
    }
    

    修改Startup.cs中的ConfigurationServices:

    services.AddMvc(options =>
    {
        // 添加全局异常
        options.Filters.Add<MvcGlobalExceptionFilter>();
    }).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    

    修改Controller文件,手动抛出异常:

    [ServiceFilter(typeof(TestServiceFilter))]
    public IActionResult Index()
    {
        throw new Exception("异常测试,这是手动抛出的异常");
        return View();
    }
    

    运行测试,效果如下:

    可以看到,我们定义的过滤器捕获并打印了异常信息。

  • 相关阅读:
    《构建之法》读书笔记⑤
    《构建之法》读书笔记④
    个人总结
    构建之法阅读笔记03
    构建之法阅读笔记02
    构建之法阅读笔记01
    第二阶段冲刺——seven
    第二阶段冲刺——six
    第二阶段冲刺——five
    第二阶段冲刺——four
  • 原文地址:https://www.cnblogs.com/diwu0510/p/9749036.html
Copyright © 2011-2022 走看看