MVC中的Filter對於整體開發來說總是有著畫龍點睛的效果;一個網站中的Filter並不會很多,但是每個Filter總是能夠完美的發揮它關鍵性的角色,它其實是一個很特別的東西,你可以決定將它套用到整個網站,或是細到某個Action。無論是使用MVC目前現成的Filter或是自行開發Filter,套用Filter其實在簡單不過,因為只需要將其當成一般的屬性標籤,直接將它放在某個Controller或是某個Action上頭就能達到效果。
Filter如果是想要套用到整個網站,需進行Global的註冊行為,其位置就在App_Start資料夾底下的FilterConfig.cs中,我們打開FilterConfig.cs來看看裡面的內容。
public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute()); } }
在靜態方法RegisterGlobalFilters中,僅有一行程式碼,該程式就是將HandleErrorAttribute這個MVC提供的Filter進行Global的註冊,亦即,HandleError這個Filter會套用到整個網站。
在MVC的專案中,我們可以看到有一個資料夾名為:Filters,該資料夾裡面所放置的就是開發人員自行撰寫的Filter。在預設範本程式碼中,我們可以看到Filters資料夾裡面有一個InitializeSimpleMember.cs檔案,打開該檔案其程式碼如下:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public sealed class InitializeSimpleMembershipAttribute : ActionFilterAttribute { private static SimpleMembershipInitializer _initializer; private static object _initializerLock = new object(); private static bool _isInitialized; public override void OnActionExecuting(ActionExecutingContext filterContext) { // Ensure ASP.NET Simple Membership is initialized only once per app start LazyInitializer.EnsureInitialized(ref _initializer, ref _isInitialized, ref _initializerLock); } private class SimpleMembershipInitializer { public SimpleMembershipInitializer() { Database.SetInitializer(null); try { using (var context = new UsersContext()) { if (!context.Database.Exists()) { // Create the SimpleMembership database without Entity Framework migration schema ((IObjectContextAdapter)context).ObjectContext.CreateDatabase(); } } WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true); } catch (Exception ex) { throw new InvalidOperationException("The ASP.NET Simple Membership database could not be initialized. For more information, please see http://go.microsoft.com/fwlink/?LinkId=256588", ex); } } } }
該程式碼的目的很簡單:當資料庫檔案不存在的時候,自動建立一個資料庫檔案供會員管理使用。由此解了我們前幾篇曾經提到的疑問,為什麼按下了網頁上的註冊之後,在App_Data的實體路徑下會多出兩個和資料庫相關的檔案。其原由就是出在這個Filter。而這個Filter套用在AccountController。
[Authorize] [InitializeSimpleMembership] public class AccountController : Controller { …省略… }
所以當使用者按下了會與AccountController產生戶動的任何頁面元件時,這個Filter就會自動啟動並進行一連串的動作。其實自行撰寫MVC的Filter很簡單,但是要先來瞭解MVC的Filter其內部的運作模式,這樣一來就不會完全摸不著頭緒了。
其實,一個Action從Request被Router導過來一直到Action處理完畢並將最後一棒交遞給View,其流程圖如下:
- OnActionExecuting(Action處理前)
- OnActionExecuted(Action處理後)
- OnResultExecuting(結果View處理前)
- OnResultExecuted(結果View處理後)
在Filter的開發上,主要有四大類,而上述的四個步驟就是其中兩大類要處理的目標。
- Authorization(針對驗證/授權處理)
- Action(針對Action處理時機作特製: Executing/Executed)
- Result(針對View處理時機作特製: Executing/Executed)
- Exception(針對例外作處理)
這四大類各有各自要實作的介面,下圖描述那一類該實作那種介面:
要開發一個自定義的Filter,僅需繼承ActionFilter再依自己要針對四大類那一類進行處理就去實作該類的介面。舉個例來說,如果我想要自行定義一個驗證相關的ActionFilter,則程式碼概略如下:
public class LogAttibute:ActionFilterAttribute,IActionFilter { public override void OnActionExecuting(ActionExecutingContext filterContext) { …省略… } }
而要套用這個自定義的Filter很簡單;若要是想要套用在HomeController上,則簡略程式碼如下:
[LogAttribute] public class HomeController : Controller { …省略…}
若想要將這個自定義的Filter套用到全網站,則需要App_Start資料夾底下的FiltersConfig.cs中加入以下程式碼:
public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute()); filters.Add(new LogAttribute()); } }
總結:在MVC中Filter是一個不可或缺的工具,善用它可以事半功倍。
沒有留言:
張貼留言