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 許可協議。轉載請註明出處!