一. 簡介html
MVC中的過濾器能夠說是MVC框架中的一種靈魂所在,它是MVC框架中AOP思想的具體體現,因此它以面向切面的形式無侵入式的做用於代碼的業務邏輯,與業務邏輯代碼分離,一經推出,廣受開發者的喜好。web
那麼過濾器究竟是什麼呢?它又有什麼做用呢?sql
用戶經過URL訪問Web系統不必定都能獲得相應的內容,一方面不一樣的用戶權限不一樣,另外一方面是爲了保護系統,防止被攻擊,這就是過濾器的核心所在,咱們總計一下過濾器都有哪些做用:數據庫
①:判斷用戶是否登陸以及不一樣用戶對應不一樣的權限問題。緩存
②:防盜鏈、防爬蟲。session
③:系統中語言版本的切換(本地化和國際化)。框架
④:權限管理系統中動態Action。nosql
⑤:決策輸出緩存。ide
知道到了過濾器的做用,那麼過濾器分哪幾類呢?以下圖1:測試
二. 執行順序
從上圖①可知,過濾器分四類,總共重寫了六個方法,在這六個方法裏能夠處理相應的業務邏輯,那麼若是四種過濾器的六個重寫方法同時存在,它們的執行順序是什麼呢?
首先要將OnException方法除外,該方法不和其他五個方法參與排序問題,該方法獨立存在,什麼時間報錯,何時調用。
其他三種過濾器中的五個重寫方法的執行順序:
三. 自定義實現形式
1. 直接在控制器中重寫方法或者利用控制器間的繼承
新建任何一個控制器,它均繼承Controller類,F12進入Controller類中,發現Controller類中已經實現了過濾器須要實現的接口,而且提供虛方法供咱們重寫,代碼以下:
基於以上原理,這樣在控制器級別上咱們就有兩種思路來實現過濾器。
方案一:直接在當前控制器重寫相應的過濾器方法,則該過濾器的方法做用於當前控制器的全部Action。
方案二:新建一個父類控制器,在父類控制器中重寫過濾器的方法,而後子類控制器繼承該父類控制器,則該該過濾器做用於子類控制器中的全部Action。
【該方法和接下來以特性的形式做用於控制器的效果是一致的】
1 /// <summary> 2 /// 控制器繼承該控制器,和特性做用在控制器上效果一致 3 /// </summary> 4 public class MyBaseFilterController : Controller 5 { 6 //須要用protected類型,不能用public類型 7 protected override void OnAuthorization(AuthorizationContext filterContext) 8 { 9 //1.若是保留以下代碼,則會運行.net framework定義好的身份驗證,若是但願自定義身份驗證,則刪除以下代碼 10 // base.OnAuthorization(filterContext); 11 12 //2.獲取區域名字 13 // string strAreaName = filterContext.RouteData.DataTokens["area"].ToString().ToLower(); 14 15 //3.獲取控制器做用的Controller和action的名字 16 string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName.ToLower(); 17 string actionName = filterContext.ActionDescriptor.ActionName.ToLower(); 18 filterContext.HttpContext.Response.Write("身份驗證過濾器做用於" + controllerName + "控制器下的" + actionName + "方法</br>"); 19 } 20 }
2. 自定義類繼承MVC中過濾器實現類或過濾器接口,特性的形式做用於控制器或Action
特別補充:MVC框架中的AuthorizeAttirbute、ActionFilterAttribute、HandleErrorAttribute類已經實現了過濾器對應的接口,因此咱們在自定義過濾器的時候,能夠直接繼承以上三個類;或者實現相應的接口:IAuthorizationFilter、IActionFilter、IResultFilter、IExceptionFilter。(該方案在實現相應接口的同時,須要繼承FilterAttribute,使自定義的類成爲一個特性)。
下面以繼承MVC中實現類的形式來自定義四種過濾器:
A:身份驗證過濾器
1 /// <summary> 2 /// 身份驗證過濾器 3 /// 1. 在非MVC框架項目中使用MVC過濾器,須要經過nuget把MVC的程序集添加進去 4 /// 2. 繼承AuthorizeAttribute類,而後對OnAuthorization方法進行 override 覆寫 5 /// 3. 在Action運行以前首先運行該過濾器 6 /// </summary> 7 public class MyAuthorize : AuthorizeAttribute 8 { 9 public override void OnAuthorization(AuthorizationContext filterContext) 10 { 11 //1.若是保留以下代碼,則會運行.net framework定義好的身份驗證,若是但願自定義身份驗證,則刪除以下代碼 12 // base.OnAuthorization(filterContext); 13 14 //2.獲取區域名字 15 // string strAreaName = filterContext.RouteData.DataTokens["area"].ToString().ToLower(); 16 17 //3.獲取控制器做用的Controller和action的名字 18 string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName.ToLower(); 19 string actionName = filterContext.ActionDescriptor.ActionName.ToLower(); 20 filterContext.HttpContext.Response.Write("身份驗證過濾器做用於" + controllerName + "控制器下的" + actionName + "方法</br>"); 21 } 22 }
B: 行爲過濾器
1 /// <summary> 2 /// 行爲過濾器 3 /// 1. 在非MVC框架項目中使用MVC過濾器,須要經過nuget把MVC的程序集添加進去 4 /// 2. 繼承ActionFilterAttribute類,而後對OnActionExecuting方法和OnActionExecuted方法進行 override 覆寫 5 /// 3. OnActionExecuting方法:在action方法運行以前,且OnAuthorization過濾器運行以後調用 6 /// OnActionExecuted方法:在action方法運行以後調用 7 /// </summary> 8 public class MyAction: ActionFilterAttribute 9 { 10 11 /// <summary> 12 /// 在action方法運行以前調用 13 /// </summary> 14 /// <param name="filterContext"></param> 15 public override void OnActionExecuting(ActionExecutingContext filterContext) 16 { 17 //1.若是保留以下代碼,則會運行.net framework定義好的行爲驗證,若是但願自定義行爲驗證,則刪除以下代碼 18 // base.OnActionExecuting(filterContext); 19 20 //2.獲取區域名字 21 // string strAreaName = filterContext.RouteData.DataTokens["area"].ToString().ToLower(); 22 23 //3.獲取控制器做用的Controller和action的名字 24 string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName.ToLower(); 25 string actionName = filterContext.ActionDescriptor.ActionName.ToLower(); 26 filterContext.HttpContext.Response.Write("行爲過濾器OnActionExecuting做用於" + controllerName + "控制器下的" + actionName + "方法運行以前</br>"); 27 } 28 /// <summary> 29 /// 在action方法運行以後調用 30 /// </summary> 31 /// <param name="filterContext"></param> 32 public override void OnActionExecuted(ActionExecutedContext filterContext) 33 { 34 //1.若是保留以下代碼,則會運行.net framework定義好的行爲驗證,若是但願自定義行爲驗證,則刪除以下代碼 35 // base.OnActionExecuted(filterContext); 36 37 //2.獲取區域名字 38 // string strAreaName = filterContext.RouteData.DataTokens["area"].ToString().ToLower(); 39 40 //3.獲取控制器做用的Controller和action的名字 41 string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName.ToLower(); 42 string actionName = filterContext.ActionDescriptor.ActionName.ToLower(); 43 filterContext.HttpContext.Response.Write("行爲過濾器OnActionExecuted做用於" + controllerName + "控制器下的" + actionName + "方法運行以後</br>"); 44 } 45 }
C:結果過濾器
1 /// <summary> 2 /// 結果過濾器 3 /// 1. 在非MVC框架項目中使用MVC過濾器,須要經過nuget把MVC的程序集添加進去 4 /// 2. 繼承ActionFilterAttribute類,而後對OnResultExecuting方法和OnResultExecuted方法進行 override 覆寫 5 /// 3. OnResultExecuting方法:在執行結果以後(action以後),頁面渲染以前調用 6 /// OnResultExecuted方法:在頁面渲染以後調用 7 /// </summary> 8 public class MyResult : ActionFilterAttribute 9 { 10 11 /// <summary> 12 /// action執行以後(OnActionExecuting以後),頁面渲染以前調用 13 /// </summary> 14 /// <param name="filterContext"></param> 15 public override void OnResultExecuting(ResultExecutingContext filterContext) 16 { 17 //1.若是保留以下代碼,則會運行.net framework定義好的結果驗證,若是但願自定義結果驗證,則刪除以下代碼 18 // base.OnResultExecuting(filterContext); 19 20 //該方法中沒法獲取是哪一個控制器後 21 filterContext.HttpContext.Response.Write("結果過濾器OnResultExecuting做用於action運行以後,頁面加載以前"); 22 } 23 /// <summary> 24 /// 頁面渲染以後調用 25 /// </summary> 26 /// <param name="filterContext"></param> 27 public override void OnResultExecuted(ResultExecutedContext filterContext) 28 { 29 //1.若是保留以下代碼,則會運行.net framework定義好的結果驗證,若是但願自定義結果驗證,則刪除以下代碼 30 // base.OnResultExecuted(filterContext); 31 32 //該方法中沒法獲取是哪一個控制器後 33 filterContext.HttpContext.Response.Write("結果過濾器OnResultExecuted做用於頁面渲染以後"); 34 } 35 }
D:異常過濾器
使用自定義異常處理,須要在web.config中爲system.web添加<customErrors mode="On" />節點
1 /// <summary> 2 /// 異常過濾器 3 /// 須要注意的點: 4 /// ①:若是自定義異常過濾器且須要有做用於全局,須要把FilterConfig中的 filters.Add(new HandleErrorAttribute());註釋掉, 5 /// 而後把自定義的異常過濾器添加到FilterConfig中。 6 /// ②:使用自定義異常處理,須要在web.config中爲system.web添加<customErrors mode="On" />節點 7 /// </summary> 8 public class MyException: HandleErrorAttribute 9 { 10 public override void OnException(ExceptionContext filterContext) 11 { 12 //調用框架自己異常處理器的方法 13 base.OnException(filterContext); 14 15 //獲取異常信息(能夠根據實際須要寫到本地或數據庫中) 16 var errorMsg = filterContext.Exception; 17 18 //跳轉指定的錯誤頁面 19 filterContext.Result = new RedirectResult("/error.html"); 20 } 21 }
下面展現以特性的形式做用於控制器或控制器中的Action:
3. 自定義類繼承MVC中實現類或接口,全局註冊,做用於所有控制器
若是以上兩種方式均不能知足你的過濾器的使用範圍,你能夠在App_Start文件夾下的FilterConfig類中進行全局註冊,使該過濾器做用於全部控制器中全部Action方法。
特別注意的一點是:自定義異常過濾器,須要把系統默認的filters.Add(new HandleErrorAttribute());註釋掉。
全局註冊的代碼以下:
1 public class FilterConfig 2 { 3 public static void RegisterGlobalFilters(GlobalFilterCollection filters) 4 { 5 //若是自定義異常過濾器,須要把默認的異常過濾器給註釋掉 6 //filters.Add(new HandleErrorAttribute()); 7 8 //自定義異常過濾器 9 filters.Add(new MyException()); 10 11 //全局註冊身份驗證、行爲、結果過濾器 12 //filters.Add(new MyAuthorize()); 13 //filters.Add(new MyAction()); 14 //filters.Add(new MyResult()); 15 16 //全局註冊登陸驗證(暫時註釋,使用的時候要打開) 17 //filters.Add(new CheckLogin()); 18 } 19 }
四. 結合實際案例進行代碼測試
1. 測試過濾器的執行順序
將上面的身份驗證過濾器、行爲過濾器、結果過濾器以特性的形式做用於Action上,經過斷點監控或者查看最後的輸出結果:
結果:
符合:OnAuthorization→OnActionExecuting-> Action方法執行 ->OnActionExecuted->OnResultExecuting/ -> Render View() (頁面渲染加載)->OnResultExecuted() 這一順序。
2. 全局捕獲異常,記錄錯誤日誌案例
步驟1:編寫異常過濾器,經過 var errorMsg = filterContext.Exception; 獲取異常信息,能夠寫入文本、存入數據庫、或者是Log4Net錯誤日誌框架進行處理。代碼在上面。
步驟2:在web.config中爲system.web添加<customErrors mode="On" />節點。
步驟3:添加到全局註冊文件中進行捕獲。
步驟4:在自定義的異常過濾器中添加斷點,而且本身製造一個錯誤。
捕獲到錯誤,進行頁面跳轉。
3. 登陸驗證案例
業務背景:在90%以上的Web系統中,不少頁面都是登陸成功之後才能看到的,固然也有不少頁面不須要登陸,對於須要登陸才能看到的頁面,即便你知道了訪問地址,也是不能訪問的,會退出到登陸頁面提示讓你登陸,對於不須要登陸的頁面經過URL地址能夠直接訪問。
分析:針對以上背景,過濾器對於大部分Action是須要過濾的,須要作登陸驗證,對於一小部分是不須要過濾的。
解決思路:
①:自定義一個身份驗證過濾器,進行全局註冊。
②:自定義一個Skip特性,將該特性加到不須要身份驗證的Action上。
③:重點說一下身份證過濾器中的邏輯:
a. 先判斷該Action上是否又Skip特性,若是有,中止不繼續執行;若是沒有,繼續下面的驗證邏輯。
b. 從Session中或Redis中讀取當前用戶,查看是否爲空,若是爲空,代表沒有登陸,返回到登陸頁面;若是不爲空,驗證經過,進行後面的業務邏輯。
代碼段以下:
1 /// <summary> 2 /// 校驗系統是否登陸的過濾器 3 /// 使用身份驗證過濾器進行編寫 4 /// </summary> 5 public class CheckLogin: AuthorizeAttribute 6 { 7 public override void OnAuthorization(AuthorizationContext filterContext) 8 { 9 //1. 校驗是否標記跨過登陸驗證 10 if (filterContext.ActionDescriptor.IsDefined(typeof(skipAttribute), true) 11 || filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(skipAttribute), true)) 12 { 13 //表示該方法或控制器跨過登陸驗證 14 return; 15 } 16 //2. 校驗是否登陸 17 //可使Session或數據庫或nosql 18 //這裏只是測試,全部通通當作沒有登陸來處理 19 var sessionUser = HttpContext.Current.Session["CurrentUser"];//使用session 20 if (sessionUser == null) 21 { 22 HttpContext.Current.Session["CurrentUrl"] = filterContext.RequestContext.HttpContext.Request.RawUrl; 23 //若是沒有登陸,則跳轉到錯誤頁面 24 filterContext.Result = new RedirectResult("/error.html"); 25 } 26 } 27 }