前言
在開發大項目的時候總會有相關的AOP面向切面編程的組件,而MVC(特指:Asp.Net MVC,以下皆同)項目中不想讓MVC開發人員去關心和寫類似身份驗證,日志,異常,行為截取等這部分重復的代碼,那我們可以通過AOP截取實現,而在MVC項目中我們就可以直接使用它提供的Filter的特性幫我們解決,不用自己實現復雜的AOP了。
在Asp.net Mvc中當你有以下及類似以下需求時你可以使用Filter功能
- 判斷登錄與否或用戶權限
- 決策輸出緩存
- 防盜鏈
- 防蜘蛛
- 本地化與國際化設置
- 實現動態Action
第一節:知識儲備
Asp.Net MVC提供了以下幾種默認的Filter:
Filter Type |
實現接口 |
執行時間 |
Default Implementation |
Authorization filter |
IAuthorizationFilter |
在所有Filter和Action執行之前執行 |
AuthorizeAttribute |
Action filter |
IActionFilter |
分別在Action執行之前和之后執行。 |
ActionFilterAttribute |
Result filter |
IResultFilter |
分別在Action Result執行之后和之前 |
ResultFilterAttribute |
Exception filter |
IExceptionFilter |
只有在filter, 或者 action method, 或者 action result 拋出一個異常時候執行
|
HandleErrorAttribute |
大家注意一點,Asp.Net MVC提供的ActionFilterAttribute默認實現了IActionFilter和IResultFilter。而ActionFilterAttribute是一個Abstract的類型,所以不能直接使用,因為它不能實例化,所以我們想使用它必須繼承一下它然后才能使用。
Filter繼承於ActionFilterAttribute抽象類,並可以覆寫 void OnActionExecuting(ActionExecutingContext) 和 void OnActionExecuted(ActionExecutedContext) 以及 void OnResultExecuting(ResultExecutingContext)和 void OnResultExecuted(ResultExecutedContext)。
它們的執行先后順序如下:
OnActionExecuting是Action執行前的操作
OnActionExecuted則是Action執行后的操作
OnResultExecuting是解析ActionResult前執行
OnResultExecuted是解析ActionResult后執行
接下來我們只要對以上的方法進行重寫就可以在相應的步驟做一些操作了。
第二節:Filter實戰
光說不練假把式,下面我給大家一個示例,來看看它們的執行順序
首先添加一個普通的類,這個類要繼承ActionFilterAttribute,,直接上代碼了
public class TestFilterAttribute:ActionFilterAttribute { public string Message { get; set; } public override void OnActionExecuting(ActionExecutingContext filterContext) { base.OnActionExecuting(filterContext); filterContext.HttpContext.Response.Write("Action執行之前"+Message+"<br />"); } public override void OnActionExecuted(ActionExecutedContext filterContext) { base.OnActionExecuted(filterContext); filterContext.HttpContext.Response.Write("Action執行之后"+Message+"<br />"); } public override void OnResultExecuting(ResultExecutingContext filterContext) { base.OnResultExecuting(filterContext); filterContext.HttpContext.Response.Write("返回Result之前"+Message+"<br />"); } public override void OnResultExecuted(ResultExecutedContext filterContext) { base.OnResultExecuted(filterContext); filterContext.HttpContext.Response.Write("返回Result之后"+Message+"<br />"); } }
寫完這個代碼后,我們回到Action上,打上上面的標記如下所示:
[TestFilter(Message="Action")] public ActionResult Index() { HttpContext.Response.Write("Action正在執行···<br />"); return Content("正在返回Result···<br />"); }
然后通過瀏覽器訪問上面的Action便可以看到下面的執行順序
總的執行順序是:
Action執行前:OnActionExecuting方法先執行→Action執行→OnActionExecuted方法執行→OnResultExecuting方法執行→返回的ActionRsult中的executeResult方法執行→OnResultExecuted執行。最終顯示的效果就是如上圖所示。
感覺很爽吧!呵呵!這要想用到這個過濾機制的地方的時候,只要在Action上面添加標記便可以實現效果。
如果我們將此標簽打到Controller上的話,TestFilterAttributeFilter將作用到Controller下的所有的Action。例如如下代碼所示:
[TestFilter(Message="Controller")] public class TestFilterController : Controller { // // GET: /TestFilter/ [TestFilter(Message="Action")] public ActionResult Index() { HttpContext.Response.Write("Action正在執行···<br />"); return Content("正在返回Result···<br />"); } }
如果單純的按照上面的代碼來做就有個問題了我們再執行顯示的頁面會有什么情況呢?Controller上的Filter會執行嗎?那標簽的作用會執行兩次嗎?下面是最后的執行結果如下圖所示:
結果說明:默認情況下Action上打了TestFilterAttribute 標簽后,雖然在Controller上也打上了此標簽,但它只有Action上的標簽起作用了。
補充:如果Action沒有打上TestFilterAttribute標簽,那么Controller上的標簽便會被執行。
Index 執行時,Filter的方法只執行了一次,而某些情況下我們也想讓Controller上的FilterAttribute也執行一次TestFilterAttribute,那我們怎么才能讓Controller上的[TestFilter(Message = "controller")]也起作用呢?
答案是:我們只需在TestFilterAttribute類的定義上打上標記[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]即可【下面類的最上面紅色字體部分】,也就是讓其成為可以多次執行的Action。代碼如下:
[AttributeUsage(AttributeTargets.All,AllowMultiple = true)] public class TestFilterAttribute:ActionFilterAttribute { public string Message { get; set; } public override void OnActionExecuting(ActionExecutingContext filterContext) { base.OnActionExecuting(filterContext); filterContext.HttpContext.Response.Write("Action執行之前"+Message+"<br />"); } public override void OnActionExecuted(ActionExecutedContext filterContext) { base.OnActionExecuted(filterContext); filterContext.HttpContext.Response.Write("Action執行之后"+Message+"<br />"); } public override void OnResultExecuting(ResultExecutingContext filterContext) { base.OnResultExecuting(filterContext); filterContext.HttpContext.Response.Write("返回Result之前"+Message+"<br />"); } public override void OnResultExecuted(ResultExecutedContext filterContext) { base.OnResultExecuted(filterContext); filterContext.HttpContext.Response.Write("返回Result之后"+Message+"<br />"); } }
瀏覽效果如下圖:
我們看到的結果是Controller上的ActionFilter先於Action上打的標記執行。同樣Result執行executeResult方法之前也是先執行Controller上的Filter標記中的OnResultexecuteing方法。
最后的執行順序是:Controller上的OnActionExecuting→Action上的OnActionExecuting→Action執行→Action上的OnActionExecuted→Controller上的OnActionExecuted
到此Action就執行完畢了,我們看到是一個入棧出棧的順序。后面是Action返回ActionResult后執行了ExecuteResult方法,但在執行之前要執行Filter。具體順序為:
接上面→Controller的OnResultExecuting方法→Action上的OnResultExecuting→Action返回ActionResult后執行了ExecuteResult方法→Action上的OnResultExecuted執行→Controller上的OnResultExecuted執行→結束。
又接着一個問題也來了,我們想有些公共的方法需要每個Action都執行以下,而在所有的Controller打標記是很痛苦的。幸好Asp。Net MVC3帶來了一個美好的東西,全局Filter。而怎么注冊全局Filter呢?答案就在Global.asax中。讓我們看以下代碼,我是如何將上面我們定義的TestFilterAttribute 注冊到全局Filter中。上代碼:
public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute()); //注冊全局過濾器 filters.Add(new TestFilterAttribute() { Message="全局"}); }
效果如下圖:
我們看到的結果是全局的Action首先執行,然后才是Controller下的Filter執行,最后才是Action上的標簽執行。當然這是在TestFilterAttribute類的定義上打上標記[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]的前提下。不然 如果Action打上了標簽跟Controller的相同則它只會執行Action上的Filter。
下面我們說幾個系統的Filter
三、AcceptVerbs
規定頁面的訪問形式,如
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Example(){
return View();
}
頁面只能以Post形式訪問,即表單提交。
四、ActionName
規定Action的名稱。
應用場景:如果不想用方法名做為Action名,或Action名為關鍵字的話,如
[ActionName("class")]
public ActionResult Example(){
return View();
}
五、NonAction
當前方法僅是普通方法不解析為Action
六、OutputCache
為Action添加緩存
[OutputCache(Duration = 60, VaryByParam = "*")]
public ActionResult Example()
{
return View();
}
七、ValidateInput
該Action可以接受Html等危險代碼(ASP.NET MVC在aspx中設置<%@ Page 的屬性無法完成等同任務。)
[ValidateInput(false)]
public ActionResult Example()
{
return View();
}
八、ValidateAntiForgeryTokenAttribute
用於驗證服務器篡改。
[ValidateAntiForgeryToken]
public ActionResult Example()
{
return View();
}
總結
經過這一篇文章的介紹我們大體了解了Filter的使用方法,還了解到全局Filter的用法,尤其是當相同的Filter重復作用到同一個Action上時,如果沒有設置可多次執行的標簽那只有Action上的Filter執行,而Controller和全局Filter都被屏蔽掉,但是設置可多次執行,那首先執行全局Filter其次是Controller再次之就是Action上的Filter了。同時還了解了系統的Filter的用法。
作者:boruipower
出處:http://www.cnblogs.com/boruipower
QQ: 1318854011