Asp.Net Core Filter 深入淺出的那些事-AOP

一、前言

在分享ASP.NET Core Filter 使用之前,先來談談AOP,什麼是AOP 呢?

AOP全稱Aspect Oriented Programming意爲面向切面編程,也叫做面向方法編程,是通過預編譯方式和運行期動態代理的方式實現不修改源代碼的情況下給程序動態統一添加功能的技術。

AOP技術利用一種稱爲“橫切”的技術,剖解開封裝對象的內部,將影響多個類的公共行爲封裝到一個可重用的模塊中,並將其命名爲Aspect切面。所謂的切面,簡單來說就是與業務無關,卻爲業務模塊所共同調用的邏輯,將其封裝起來便於減少系統的重複代碼,降低模塊的耦合度,有利用未來的可操作性和可維護性。

利用AOP可以對業務邏輯各個部分進行隔離,從而使業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高開發效率。

AOP的使用場景主要包括日誌記錄、性能統計、安全控制、事務處理、異常處理等。

二、Filter-過濾器

Filter是延續ASP.NET MVC的產物,同樣保留了五種的Filter,分別是Authorization Filter、Resource Filter、Action Filter、Exception Filter及Result Filter。
通過不同的Filter可以有效處理封包進出的加工,本篇將介紹ASP.NET Core的五種Filter運作方式。

2.1 Filter 介紹

ASP.NET Core 有以下五種Filter 可以使用:

  • Authorization Filter:
    Authorization是五種Filter中優先級最高的,通常用於驗證Request合不合法,不合法後面就直接跳過。
  • Resource Filter:Resource是第二優先,會在Authorization之後,Model Binding之前執行。通常會是需要對Model加工處理才用。
  • Exception Filter:異常處理的Filter。
  • Action Filter:最常使用的Filter,封包進出都會經過它,使用上沒什麼需要特別注意的。跟Resource Filter很類似,但並不會經過Model Binding。
  • Result Filter:當Action完成後,最終會經過的Filter。

三、五大Filter 的應用

這一篇章主要來講解Asp.Net Core 的五大過濾器的實現及用途.

3.1 Authonization Filter

權限控制過濾器
通過 Authonization Filter 可以實現複雜的權限角色認證登陸授權等操作
實現事例代碼如下:

    public class AuthonizationFilter :Attribute,IAuthorizationFilter
    {
        public void OnAuthorization(AuthorizationFilterContext context)
        {
            //這裏可以做複雜的權限控制操作
            if (context.HttpContext.User.Identity.Name != "1") //簡單的做一個示範
            {
                //未通過驗證則跳轉到無權限提示頁
                RedirectToActionResult content = new RedirectToActionResult("NoAuth", "Exception", null);
                context.Result = content;
            }
        }
    }

3.2 Resource Filter

資源過濾器
可以通過Resource Filter 進行資源緩存防盜鏈等操作。
使用Resource Filter 要求實現IResourceFilter 抽象接口

   public class ResourceFilter : Attribute,IResourceFilter
    {
        public void OnResourceExecuted(ResourceExecutedContext context)
        {
            // 執行完後的操作
        }

        public void OnResourceExecuting(ResourceExecutingContext context)
        {
            // 執行中的過濾器管道
        }
    }

3.3 Exception Filter

通過Execption Filter 過濾器可以進行全局的異常日誌收集 等操作。

使用Execption Filter 要求實現IExceptionFilter 抽象接口
IExceptionFilter接口會要求實現OnException方法,當系統發生未捕獲異常時就會觸發這個方法。OnException方法有一個ExceptionContext異常上下文,其中包含了具體的異常信息,HttpContext及mvc路由信息。系統一旦出現未捕獲異常後,比較常見的做法就是使用日誌工具,將異常的詳細信息記錄下來,方便修正調試。下面是日誌記錄的實現。

  public class ExecptionFilter : Attribute, IExceptionFilter
  {
        private ILogger<ExecptionFilter> _logger;
        //構造注入日誌組件
        public ExecptionFilter(ILogger<ExecptionFilter> logger)
        {
            _logger = logger;
        }

        public void OnException(ExceptionContext context)
        {
            //日誌收集
            _logger.LogError(context.Exception, context?.Exception?.Message??"異常");
        }
    }

3.4 Action Filter

作用:可以通過ActionFilter 攔截 每個執行的方法進行一系列的操作,比如:執行操作日誌參數驗證權限控制 等一系列操作。

使用Action Filter 需要實現IActionFilter 抽象接口,IActionFilter 接口要求實現OnActionExecutedOnActionExecuting 方法

    public class ActionFilter : Attribute, IActionFilter
    {
        public void OnActionExecuted(ActionExecutedContext context)
        {
            //執行完成....
        }

        public void OnActionExecuting(ActionExecutingContext context)
        {
            //執行中...
        }
    }

3.5 Result Filter

結果過濾器,可以對結果進行格式化、大小寫轉換等一系列操作。

使用Result Filter 需要實現IResultFilter 抽象接口,接口要求實現
OnResultExecuting 方法 和OnResultExecuted 方法

  • OnResultExecuting :Called before the action result executes. 在操作結果執行之前調用
  • OnResultExecuted :Called after the action result executes. 在操作結果執行之後調用

具體代碼實現代碼如下:

public class ResultFilter : Attribute, IResultFilter
 {
        public void OnResultExecuted(ResultExecutedContext context)
        { 
            // 在結果執行之後調用的操作...
        }

        public void OnResultExecuting(ResultExecutingContext context)
        {
            // 在結果執行之前調用的一系列操作
        }
    }

四、Asp.Net Core 過濾器的註冊方式

這一篇章主要來分析探討Asp.Net Core 中過濾器的三種註冊方式ActionController全局

4.1 Action 註冊方式

Action 註冊方式是局部註冊方式,針對控制器中的某個方法上標註特性的方式進行註冊,代碼如下:

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

4.2 Controller 註冊方式

瞭解過Action 特性註冊方式的同學,一定發現了它的不好之處就是我一個控制器裏面需要使用同一套Filter 的時候,需要一個一個Action 標註特性註冊,是不是很繁瑣呢?有沒有其他方式進行代替這些繁瑣的操作呢?微軟給我們提供了簡便的控制器標註註冊方式,代碼如下:

 [AuthonizationFilter()]
 public class FirstController : Controller
  {
        private ILogger<FirstController> _logger;

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

        public IActionResult Index()
        {
            return View();
        }
 }

4.3 全局註冊方式

現在有些同學考慮了一些全局的情況,比如我要全局處理系統中的異常,或者收集操作日誌等,需要全局註冊一個ExceptionFilter 來實現,就不需要每一個Controller 中進行代碼註冊,方便快捷。代碼如下:

 public void ConfigureServices(IServiceCollection services)
  {
            //全局註冊異常過濾器
            services.AddControllersWithViews(option=> {
                option.Filters.Add<ExecptionFilter>();
            });

            services.AddSingleton<ISingletonService, SingletonService>();
}

4.4 TypeFilter 和 ServiceFilter 註冊方式

上面的五大過濾器中事例代碼中其中有一個過濾器的代碼比較特,再來回顧ExceptionFilter過濾器的實現代碼:

    public class ExecptionFilter : Attribute, IExceptionFilter
    {
        private ILogger<ExecptionFilter> _logger;
        //構造注入日誌組件
        public ExecptionFilter(ILogger<ExecptionFilter> logger)
        {
            _logger = logger;
        }

        public void OnException(ExceptionContext context)
        {
            //日誌收集
            _logger.LogError(context.Exception, context?.Exception?.Message??"異常");
        }
    }

從上面的代碼中可以發現 ExceptionFilter 過濾器實現中存在日誌服務的構造函數的注入,也就是說該過濾器依賴於其他的日誌服務,但是日誌服務都是通過DI 注入進來的;再來回顧下上面Action 註冊方式或者Controller 註冊方式 也即Attribute 特性標註註冊方式,本身基礎的特性是不支持構造函數的,是在運行時註冊進來的,那要解決這種本身需要對服務依賴的過濾器需要使用 TypeFilter 或者ServiceFilter 方式進行過濾器的標註註冊。

TypeFilterServiceFilter 的區別。

  • ServiceFilter和TypeFilter都實現了IFilterFactory
  • ServiceFilter需要對自定義的Filter進行註冊,TypeFilter不需要
  • ServiceFilter的Filter生命週期源自於您如何註冊,而TypeFilter每次都會創建一個新的實例

TypeFilter 使用方式

代碼如下:

[TypeFilter(typeof(ExecptionFilter))]
public IActionFilter Index2()
{
      return View();
}

通過上面的代碼可以發現AuthonizationFilter 是默認的構造器,但是如果過濾器中構造函數中存在參數,需要注入服務那該怎麼辦呢?,比如上面的ExceptionFilter 代碼,就不能使用這種方式進行註冊,需要使用服務特性的方式,我們可以選擇使用 代碼如下:

[TypeFilter(typeof(ExecptionFilter))]
public IActionFilter Index2()
{
           return View();
}

ServiceFilter 使用方式

控制器中的代碼如下:

[ServiceFilter(typeof(ExecptionFilter))]
public IActionFilter Index2()
{
           return View();
}

註冊服務的代碼如下:

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
       Console.WriteLine("ConfigureServices");
       services.AddControllersWithViews();

       //services.AddControllersWithViews(option=> {
       //    option.Filters.Add<ExecptionFilter>();
       //});
        
        //註冊過濾器服務,使用ServiceFilter 方式必須要註冊 否則會報沒有註冊該服務的相關異常
        services.AddSingleton<ExecptionFilter>();
}

如果您覺的不錯,請微信掃碼關注 【dotNET 博士】公衆號,後續給您帶來更精彩的分享

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章