第四節:MVC中AOP思想的體現(四種過濾器)並結合項目案例說明過濾器的實際用法

一. 簡介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     }
View Code

 

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     }
View Code

 

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     }
View Code

 

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     }
View Code

 

下面展現以特性的形式做用於控制器或控制器中的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     }
相關文章
相關標籤/搜索