如果我想給所有Action注入一段html到頁面底部,我必須去修改Controller嗎?
如果我想動態(tài)控制某個Action允許由哪些角色訪問,我通過修改Controller能實現(xiàn)嗎?
如果我想這時候控制的由哪些角色來訪問,需求改變時我想要控制由哪些用戶來訪問呢?我還得去修改Controller嗎?或者增加或修改Filter嗎?
所以需求就出現(xiàn)了:能不能讓我集中管理這些Filter,并且控制的更靈活呢?能讓所有Controller和Action簽名上都干干凈凈的那就最好了。
那就把Filter放在一處集中管理,在Action或ActionResult等執(zhí)行Filter之前保證將需要的Filter準備好就行了。
在解決問題之前,先簡單回顧一下Action執(zhí)行前后發(fā)生的事。
我們知道,在ASP.NET MVC中,每一次請求通常都定位到一個具體的Controller的Action中。
在默認情況下,由Action執(zhí)行器ControllerActionInvoker類去控制Action的執(zhí)行(或不執(zhí)行),實際做事的是InvokeAction方法。
InvokeAction方法首先去查找Action(由FindAction方法),如果Action被找到了,會去檢索該Action擁有的Filter以及該Action所屬Controller的Filter(由GetFilters方法),將找到的所有Filter放入一個FilterInfo變量中,F(xiàn)ilterInfo中保存的Filter不完全是Action是自己的。然后先執(zhí)行找到的所有IAuthorizationFilter(由InvokeAuthorizationFilters方法)。當然,在InvokeAuthorizationFilters方法中,只要有IAuthorizationFilter的ActionResult不為null就會返回不會執(zhí)行其他的了。 InvokeAction方法檢測執(zhí)行結果,如果ActionResult的確不為空,則執(zhí)行該Result,其他Filter就不管啦。 接著獲取要傳給Action的參數(shù)集(交給GetParameterValue方法),就執(zhí)行InvokeActionMethodWithFilters方法,方法名已經足夠說明它是干什么的了。如果一切正常,根據(jù)InvokeActionMethodWithFilters方法返回的結果去接著就執(zhí)行InvokeActionResultWithFilters方法。在執(zhí)行InvokeAuthorizationFilters一直到執(zhí)行InvokeActionResultWithFilters的這一整個過程中如果發(fā)生異常,則根據(jù)捕獲的異常執(zhí)行InvokeExceptionFilters進行異常處理。
這里需要注意一點:InvokeAction、GetFilters、InvokeAuthorizationFilters、GetParameterValue、InvokeActionMethodWithFilters、InvokeActionResultWithFilters、InvokeExceptionFilters等全是虛方法,除非有足夠的原因去繼承IActionInvoker重寫一個Action執(zhí)行器,否則重寫某些方法足夠擴展。
甚至ControllerActionInvoker類本身,在ASP.NET MVC基礎架構中也是可以替換的,怎么替換呢?繼承Controller類時重寫CreateActionInvoker方法就可以。
另外還可以在構造Controller對象給它的ActionInvoker屬性賦值,這又怎么賦值?重寫DefaultControllerFactory創(chuàng)建Controller實例的GetControllerInstance方法。 然后在Applicaion_Start中設置新的ControllerFactor:
ControllerBuilder.Current.SetControllerFactory(new YourControllerFactory());
回到主題。 首先我們將Filter和Action的對應關系(Filter和Controller的對應關系在本文中暫不討論)存于一個集合中并緩存起來。
從找到Action到執(zhí)行InvokeAuthorizationFilters之前,必須將IAuthorizationFilter準備好;從找到Action到執(zhí)行InvokeActionMethodWithFilters之前,必須將Action需要的IActionFilter準備好;從找到Action到執(zhí)行InvokeActionResultWithFilters之前,必須將ActionResult需要的IResultFilter準備好。異常發(fā)生InvokeExceptionFilters執(zhí)行之前,必須將IExceptionFilter準備好。 基于以上幾點,我們好像只能在FindAction方法和GetFilters方法之間選擇一個。當然也就是只有GetFilters了。
在Filter方法中,我們根據(jù)當前Action的特征(如方法名,或包括請求方式Get或Post)與Filter和Action對應表。將匹配的Filter加進FilterInfo變量中。