ASP.NET MVC學習之過濾器篇(2)


下面我們繼續之前的ASP.NET MVC學習之過濾器篇(1)進行學習。

 

3.動作過濾器

顧名思義,這個過濾器就是在動作方法調用前與調用后響應的。我們可以在調用前更改實際調用的動作,也可以在動作調用完成之后更改最終返回的結果,當然很多人一定不太明白這個到底可以干什么,

下面我們舉一個比較實際的例子:

 

相信理解過網站的安全的一定知道跨站請求(CSRF具體可以自行百度,這里我就不去解釋了),當然也有解決方案,那就是給頁面中增加一個識別碼,當頁面進行POST請求時,首先判斷識別碼是否正確,

如果正確則繼續進行操作。並且在執行完成之后重新分配一個新的識別碼(當然也可以只用一個識別碼直到會話結束),這樣就可以加大進行跨站請求的難度。而動作過濾器的所處的生命周期剛好符合,

下面我們就開始編寫這個過濾器類。

 

首先我們在Filter文件夾中新建一個ViewMacFilterAttribute類,並且這個類需要繼承FilterAttribute,同時還要實現IActionFilter接口:

 1 namespace MvcStudy.Filter
 2 {
 3     public class ViewMacFilterAttribute : FilterAttribute , IActionFilter
 4     {
 5 
 6         public void OnActionExecuted(ActionExecutedContext filterContext)
 7         {
 8             string viewMac = Guid.NewGuid().ToString();
 9             filterContext.HttpContext.Session["vmac"] = viewMac;
10             filterContext.Controller.ViewBag.ViewMac = viewMac;
11         }
12 
13         public void OnActionExecuting(ActionExecutingContext filterContext)
14         {
15             object viewMac = filterContext.HttpContext.Session["vmac"];
16             string strMac = filterContext.HttpContext.Request.Form["viewMac"];
17             filterContext.Result = new HttpNotFoundResult();
18             if (viewMac != null && strMac != null)
19             {
20                 if (viewMac.Equals(strMac))
21                 {
22                     filterContext.Result = null;
23                 }
24             }
25         }
26     }
27 }

 這里我們實現了IActionFilter 接口中的OnActionExecuted方法和OnActionExecuting方法,它們分別對應着動作執行結束和動作執行前,通過OnActionExecuting中的代碼,我們可以清楚的看到我們首先從Session中獲取識別碼,然后又從頁面中的表單獲取客戶端的識別碼,

這里我們可以看到我們首先默認返回的結果是404,如果比配成功則將Result設置為NULL,如果ResultNULL是會正常調用對應的活動的。接着就是OnActionExecuted方法中的功能,僅僅只是重新分配一個識別碼,並且保存進Session中。

 

注:這種方式會導致一個頁面進行了POST請求之后,識別碼變換之后。其他頁面的提交功能就會失敗,所以還要加以ajax去輔助,或者就是一個用戶會話分配一個識別碼,直到用戶會話結束。

 

有了這個過濾器之后我們就可以進行實際的測試了,首先我們先在Home控制器中寫入如下代碼,並在Views/Home下新建Index.cshtml以及List.cshtml,其中List.cshtml中的頁面如下所示:

 1 @{
 2     ViewBag.Title = "List";
 3 }
 4 
 5 <h2>List</h2>
 6 @using(Html.BeginForm())
 7 {
 8     @Html.Hidden("viewMac", (string)ViewBag.ViewMac)
 9     <input type="submit" value="submit" />
10 }

 

 

然后就是Home控制器:

 1 namespace MvcStudy.Controllers
 2 {
 3     public class HomeController : Controller
 4     {
 5         [MobilResultFilter]
 6         public ActionResult Index()
 7         {
 8 
 9             return View();
10         }
11 
12 
13         public ActionResult List()
14         {
15             string viewMac = Guid.NewGuid().ToString();
16             Session["vmac"] = viewMac;
17             ViewBag.ViewMac = viewMac;
18             return View();
19         }
20 
21         [ViewMacFilter]
22         [HttpPost]
23         public ActionResult List(string action)
24         {
25             return View();
26         }
27     }
28 }

這里我們可以看到默認的List動作中會分配識別碼,而POST請求對應的List方法加上了我們之前寫的過濾器,現在我們可以先打開這頁面然后提交,會發現只是刷新了,接着我們把List.cshtml中的@Html.Hidden("viewMac", (string)ViewBag.ViewMac) 刪去,重新進入這個頁面,在點擊提交,就能發現出現了404的錯誤,這樣我們就可以防止跨站請求了。

 

 

4.結果過濾器

上面講的僅僅只是動作過濾器,在動作執行完成之后都會返回一個結果,即使到這里了,我們依然可以修改最終的結果,之前在講路由器部分的時候,曾今舉過一個例子,就是根據UserAgent去判斷是否為手機,並跳轉到對應的控制器下的對應動作。但是很多時候手機頁面與PC頁面的數據都是一摸一樣的,如果像之前那樣就會導致重復代碼的出現,那么我們就可以利用結果過濾器,在動作執行完成之后,判斷是否為手機從而改變最后呈現的視圖。

 

下面我們繼續在Filter文件夾下新建一個MobilResultFilterAttribute類,並且這個類依然要繼承FilterAttribute,同時還要實現IResultFilter接口,實現代碼如下:

 1 namespace MvcStudy.Filter
 2 {
 3     public class MobilResultFilterAttribute : FilterAttribute , IResultFilter
 4     {
 5 
 6         public void OnResultExecuted(ResultExecutedContext filterContext)
 7         {
 8 
 9         }
10 
11         public void OnResultExecuting(ResultExecutingContext filterContext)
12         {
13             if (filterContext.HttpContext.Request.UserAgent.Contains("Android"))
14             {
15                 ((ViewResult)filterContext.Result).ViewName = "List";
16             }
17         }
18     }
19 }

 為了能夠以最簡單的方式說明,所以筆者僅僅判斷了UserAgent是否含有Android,如果含有就修改最終的視圖名稱,這樣我們就可以通過注解屬性的方式實現,而不需要放在控制器中或者在動作中進行判斷。

 

下面我們依然要以測試為主,我們還是使用Home控制器:

1         [MobilResultFilter]
2         public ActionResult Index()
3         {
4 
5             return View();
6         }

 首先我們通過正常的方式訪問,然后讓Chrome模擬手機進行訪問,這個時候我們就可以發現最終的頁面會是List而不是Index

 

上面的3和4我們都要自己去繼承對應的接口,同時還要繼承FilterAttribute類,其實ASP.NET MVC中已經為我們寫好了一個默認的類,就是ActionFilterAttribute,我們源碼中的定義:

1 public abstract class ActionFilterAttribute : FilterAttribute, IActionFilter, IresultFilter

 它已經繼承了FilterAttribute,同時也實現了IActionFilterIResultFilter接口,這樣可以可以少記一些關鍵的名稱。

 

 

5.無注解屬性過濾與全局過濾器

通過之前的幾點,大家可以發現我們都是使用注解屬性的方式進行過濾,其他我們也可以直接在控制器中實現,比如下面的代碼:

 1 namespace MvcStudy.Controllers
 2 {
 3     public class HomeController : Controller
 4     {
 5         protected override void OnActionExecuted(ActionExecutedContext filterContext)
 6         {
 7             base.OnActionExecuted(filterContext);
 8         }
 9 
10         protected override void OnActionExecuting(ActionExecutingContext filterContext)
11         {
12             base.OnActionExecuting(filterContext);
13         }
14 
15         protected override void OnAuthorization(AuthorizationContext filterContext)
16         {
17             base.OnAuthorization(filterContext);
18         }
19 
20         protected override void OnException(ExceptionContext filterContext)
21         {
22             base.OnException(filterContext);
23         }
24 
25         protected override void OnResultExecuted(ResultExecutedContext filterContext)
26         {
27             base.OnResultExecuted(filterContext);
28         }
29 
30         protected override void OnResultExecuting(ResultExecutingContext filterContext)
31         {
32             base.OnResultExecuting(filterContext);
33         }
34 
35         [MobilResultFilter]
36         public ActionResult Index()
37         {
38 
39             return View();
40         }
41 
42 
43         public ActionResult List()
44         {
45             string viewMac = Guid.NewGuid().ToString();
46             Session["vmac"] = viewMac;
47             ViewBag.ViewMac = viewMac;
48             return View();
49         }
50 
51         [ViewMacFilter]
52         [HttpPost]
53         public ActionResult List(string action)
54         {
55             return View();
56         }
57     }
58 }

我們可以看到控制器中其實已經實現了上面所有的接口,當然我建議使用注解屬性,這樣可以盡量的分離關注點,控制器的代碼會很雜亂。

 

關於全局過濾器,唯一的區別就是我們需要在FilterConfig類中的RegisterGlobalFilters方法中使用filters.add增加上面我們所寫的過濾器即可,並沒有太大的特殊(FilterConfig在App_Start,在ASP.NET MVC 4以上才有這個文件

 

 

6.內建過濾器

關於內建過濾器我們這里就列舉出來以下,因為上手很快。

RequireHttps   強迫對動作使用Https協議

OutputCache   緩存一個動作方法的輸出

ValidateInput   與安全有關的授權過濾器

AsyncTimeout/NoAsyncTimeout  用於異步控制器

ChildActionOnly 只能作為子操作進行調用

 

到此為止關於過濾器部分就結束了。

 

下面是之前的前面ASP.NET MVC學習連載:

ASP.NET MVC學習之路由器篇(1)

ASP.NET MVC學習之路由器篇(2)

ASP.NET MVC學習之路由器篇(3)

ASP.NET MVC學習之控制器篇

ASP.NET MVC學習之過濾器篇(1)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM