【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();
}

運行測試,效果如下:

可以看到,我們定義的過濾器捕獲並打印了異常信息。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM