十年河東,十年河西,莫欺少年窮
學無止境,精益求精
今兒是周六,蘇州的天空飄着毛毛細雨,氣溫也下降了不少,上午去了蘇州繁花中心,來到二樓,自學了會古箏,逛了逛商場,中午去了肯德基,給孩子買了雞翅,我和我老婆大人各喝了一杯咖啡。下午回到家,躺在床上刷抖音,刷的時間長了,也就覺得特別無聊,索性看看博客園吧,嘿嘿,於是我買了一瓶100ML的牛欄山二鍋頭,邊吃花生米邊看,本打算看netcore的中間件,於是百度搜了一些內容,大多數寫的中間件都是參考微軟教程,看的也是索然無味,有的說中間件類似於AOP,有的說中間件是HTTP請求管道中的一個組件,用於攔截你的http請求並決定是否把你的請求傳遞給下一個中間件,總的來說,中間件就是一個橫向切面編程,也就是所謂的AOP,這里咱們不討論中間件,我們今天討論的是和中間件功能類似的過濾器,何為過濾器呢?過濾器和中間件有何細微的差別呢?哈哈,其實本篇博客是轉載的別人,原文地址:https://www.cnblogs.com/jlion/p/12394949.html
首先感謝原文作者的貢獻,其次我之所以轉載這篇博客,一是因為作者寫的好,二是因為這些知識和之前的MVC過濾器很類似,再者,我之前也寫過過濾器的應用,比如MVC的登錄授權過濾器,對MVC的登錄授權過濾器有興趣的小虎斑可參考我的博客:https://www.cnblogs.com/chenwolong/p/Attribute.html 和 https://www.cnblogs.com/chenwolong/p/Token.html 兩篇博客。
廢話說多了,下面咱們進入正題,如下:
一、前言
在分享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
接口要求實現OnActionExecuted
和OnActionExecuting
方法
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 中過濾器
的三種注冊方式Action
、Controller
、全局
。
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
方式進行過濾器的標注注冊。
TypeFilter
和ServiceFilter
的區別。
- 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>(); }
如果你覺得還不錯,就請點個贊吧,謝謝。
參考博客:
博客:https://www.cnblogs.com/jlion/p/12394949.html
知乎:https://zhuanlan.zhihu.com/p/112507159