Agenda
- 前言
- 揭密取得過濾器(Filter)機制AOP
- 五種過濾器(Filter)介面
- AuthorizationFilter
- IActionFilter方法執行前,後的過濾器
- InvokeActionResult 動作執行前,後過濾器
- IExceptionFilter錯誤過濾器
- 小結:
前言
上篇和大家介紹Filter去是如何取得且我們可以透過IOC容器註冊IFilterProvider來擴充取得Filter注入點.
在ASP.NET MVC的Filter,在執行目標前後彈性擴充額外操作(繼承ActionFilter並掛Attribute),這是一種典型的AOP設計模式
本篇會和大家繼續分享InvokeAction後續動作.
為什麼我們在Action方法和Controller類別放置一個繼承(AuthorizationFilter、ActionFilter、ResultFilter,ExceptionFilter)標籤(Attribute)對應介面(IAuthorizationFilter、IActionFilter、IResultFilter,IExceptionFilter),程式幫我們自動載入MVC生命週期中並執行?
我有做一個可以針對於Asp.net MVC Debugger的專案,只要下中斷點就可輕易進入Asp.net MVC原始碼.
揭密取得過濾器(Filter)機制AOP
AOP 是 OOP(物件導向)一個變化程式撰寫思想。(非取代OOP而是擴充)
導入AOP幫助:
可幫我們分離核心邏輯跟非核心邏輯代碼,很好降低模組間耦合性,已便日後擴充。
非核心邏輯代碼像:(日誌記錄,性能統計,安全控制,事務處理,異常處理等代碼從業務邏輯代碼中劃分出來)

原本寫法把寫日誌相關程式寫入,業務邏輯方法中。導致此方法非單一職則。我們可以把程式重構改寫成(右圖),將寫日誌方法抽離出來更有效達成模組化。
AOP(Aspect-Oriented Programming)核心概念Proxy Pattern
AOP是擴充Proxy Pattern(代理模式)概念,為每個方法提供一個代理人,可為執行前或執行後提供擴展機制,並由代理類別來呼叫真正呼叫使用方法.
如果想要更多了解代理模式可以參考我之前寫的ProxyPattern代理模式(二)
五種過濾器(Filter)介面
在Asp.net MVC有五個過濾器實現AOP架構
下面順序案照執行呼叫執行順序來介紹
IAuthenticationFilter:最一開始執行驗證使用過濾器,這個介面有一個void OnAuthentication(AuthenticationContext filterContext)方法.如果驗證失敗可以對於filterContext.Result設值來結束這次請求.IAuthorizationFilter:執行過程和IAuthenticationFilter過濾器基本上一樣IActionFilter:提供方法執行前,後的動作.IResultFilter:提供方法執行結果前,後的動作.IExceptionFilter:在執行此方法有錯誤時觸發的過濾器.
MVC上面幾個過濾器,讓開發者可以很有彈性擴充自己的系統且不用動到核心原始碼.很好達到開放封閉原則
AuthorizationFilter
AuthorizationFilter在ActionInvoker執行前第一項工作,因為後續工作(參數模型綁定,參數模型驗證,呼叫方法)只有在驗證成功的基礎上才會有意義。
IAuthenticationFilter and AuthenticationContext
一開始呼叫InvokeAuthenticationFilters方法來取得AuthenticationContext物件,在判斷authenticationContext.Result是否有給值.如果有當作驗證失敗不用在執行後面流程.
1 | try |
InvokeAuthenticationFilters方法
1 | protected virtual AuthenticationContext InvokeAuthenticationFilters( |
AuthenticationContext中重要的一個屬性是
public ActionResult Result { get; set; }只要這個物件不為null就會直接返回此次請求.
在方法中我封裝一個AuthenticationContext物件,把它當作參數傳入IAuthenticationFilter.OnAuthentication方法中(這就是我們在繼承AuthenticationFilter使用AuthenticationContext物件)
值得一提程式會判斷context.Result是否為null來當作迴圈中斷點.
1 | if (context.Result != null) |
這個邏輯是我們對於Authentication驗證失敗後想要直接返回請求可以透過把context.Result給一個值(ActionResult物件),外面會照authenticationContext.Result是否為null為依據判斷是否繼續執行後面動作.
IAuthorizationFilter and AuthorizationContext
下一個步驟是檢驗IAuthorizationFilter過濾器,執行過程和IAuthenticationFilter過濾器基本上一樣
依照物件內Result屬性是否為null來當作後續執行依據.
1 | AuthorizationContext authorizationContext = InvokeAuthorizationFilters(controllerContext, filterInfo.AuthorizationFilters, actionDescriptor); |
AuthorizationContext類別
1 | public class AuthorizationContext : ControllerContext |
既然IAuthenticationFilter和IAuthorizationFilter過濾器驗證東西都很類似為什麼要分成兩個呢?
仔細比較會發現IAuthenticationFilter多了(設置Principal),檢驗方式。
ActionDescriptor(使用ReflectedActionDescriptor)這個物件存放目前執行Action相關的資訊(裡面有一個Execute抽象方法,靠他來做Action呼叫使用)
1 | protected virtual void InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) |
如果判斷權限錯誤或Filter需提前返回Result就會執行InvokeActionResult方法,來執行返回工作.
IActionFilter方法執行前,後的過濾器
有在寫Asp.net MVC的人一定對於下面這個介面不陌生,這個過濾器在InvokeActionMethodFilter使用時被呼叫.
ActionExecutingContext也有一個Result物件用此判斷是否有執行後續請求.一般也是NULL
ActionExecutingContext這個物件比其他過濾器參數多了一個重要的成員IDictionary<string, object> parameters,有這個成員我們可以針對呼叫Action參數處理.
1 | public interface IActionFilter |
其中有一段continuation這個委派是InvokeActionMethod這個方法,這個方法取得使用Action方法.
1 | protected virtual ActionResult InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters) |
1 | try |
ActionExecutedContext物件中的Result屬性就是執行Action方法後的結果
InvokeActionResult 動作執行前,後過濾器
呼叫InvokeActionResult過濾器藉由InvokeActionResultFilterRecursive方法
這個方法使用遞迴方式看之前的使用for loop執行過濾器方式有所不同,幸好在原始碼有註解.
主要是因為下面原因
OnResultExecuting事件必須按正向順序觸,發然後必須觸發InvokeActionResult(執行Action動作方法),OnResultExecuted事件必須以相反的順序觸發
1 | private ResultExecutedContext InvokeActionResultFilterRecursive(IList<IResultFilter> filters, int filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult) |
在OnResultExecuting方法的ResultExecutingContext可以藉由Canceled這個屬性來最後控制是否要執行Action方法,如果不要將這個值設定為false.
1 | public virtual bool Canceled { get; set; } |
IExceptionFilter錯誤過濾器
最後介紹錯誤時呼叫的過濾器IExceptionFilter
可以看到在執行方法的最前面使用了一個try....catch而最後catch程式碼如下.
在這個方法中有一個重要的屬性是bool ExceptionHandled,如果在錯誤時設定為true她就會執行Result的結果(因為最後呼叫了InvokeActionResult方法.
1 | //.... |
小結:
過濾器這部分原始碼很值得大家探討,因為在主流IOC容器框架有支援AOP概念.
AOP有很大優點是可做到設計五大原則的其中兩項
使程式碼耦合性變低
執行Action方法前,如何取得權限過濾器並呼叫檢驗,另外在呼叫方法前可以看到會把用到的資訊封裝到一個Context物件中.
IAuthenticationFilter和IAuthorizationFilter基本上都是權限驗證的過濾器
但有先後順序,這點需注意!! 先執行
IAuthenticationFilter後IAuthorizationFilter
看了MVC過濾器原始碼後有感而法,石頭就基於RealProxy這個類別做了一個AOP開源框架AwesomeProxy.Net.
下篇會繼續介紹Action參數如何建立,遇到複雜Model MVC是怎麼處理
__此文作者__:Daniel Shih(石頭)
__此文地址__: https://isdaniel.github.io/Ithelp-day16/
__版權聲明__:本博客所有文章除特別聲明外,均採用 CC BY-NC-SA 3.0 TW 許可協議。轉載請註明出處!