一、過濾器(Filters)就是向請求處理管道中注入額外的邏輯。提供了一個簡單而優雅的方式來實現橫切關注點。html
二、所謂的過濾器(Filters),MVC框架裏面的過濾器徹底不一樣於ASP.NET平臺裏面的Request.Filters和Response.Filter對象,它們主要是實現請求和響應流的傳輸。一般咱們所說的過濾器是指MVC框架裏面的過濾器。安全
三、過濾器能夠注入一些代碼邏輯到請求處理管道中,是基於C#的Attribute的實現。當負責調用Action的類ControllerActionInvoker在調用執行Action的時候會檢查Action上面的Attribute並查看這些Attribute是否實現了指定的接口,以便進行額外的代碼注入處理框架
假設你作了一個小項目,其中某個功能是操做管理用戶信息模塊,有這樣一個需求,對用戶信息管理必須是已經過認證的用戶才能操做,咱們能夠在每個Action方法裏面檢查認證請求,以下所示:asp.net
1 using MvcFilterDmo.Core; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Web; 6 using System.Web.Mvc; 7 using System.Web.Security; 8 9 namespace MvcFilterDmo.Controllers 10 { 11 public class HomeController : Controller 12 { 13 public ActionResult Index() 14 { 15 if (!Request.IsAuthenticated) 16 { 17 FormsAuthentication.RedirectToLoginPage(); 18 } 19 //操做部分... 20 return View(); 21 } 22 public ActionResult Insert() 23 { 24 if (!Request.IsAuthenticated) 25 { 26 FormsAuthentication.RedirectToLoginPage(); 27 } 28 //操做部分... 29 return View(); 30 } 31 public ActionResult Update() 32 { 33 if (!Request.IsAuthenticated) 34 { 35 FormsAuthentication.RedirectToLoginPage(); 36 } 37 //操做部分... 38 return View(); 39 } 40 public ActionResult Delete() 41 { 42 if (!Request.IsAuthenticated) 43 { 44 FormsAuthentication.RedirectToLoginPage(); 45 } 46 //操做部分... 47 return View(); 48 } 49 //其餘Action操做方法 50 //... 51 } 52 }
經過上面的代碼,能夠發現使用這種方式檢查請求認證有許多重複的地方,這也就是爲何要使用過濾器的緣由,使用過濾器能夠實現相同的效果。以下所示:ide
1 using MvcFilterDmo.Core; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Web; 6 using System.Web.Mvc; 7 using System.Web.Security; 8 9 namespace MvcFilterDmo.Controllers 10 { 11 [Authorize] 12 public class HomeController : Controller 13 { 14 public ActionResult Index() 15 { 16 //操做部分... 17 return View(); 18 } 19 public ActionResult Insert() 20 { 21 //操做部分... 22 return View(); 23 } 24 public ActionResult Edit() 25 { 26 //操做部分... 27 return View(); 28 } 29 public ActionResult Delete() 30 { 31 //操做部分... 32 return View(); 33 } 34 //其餘Action操做方法 35 //... 36 } 37 }
過濾器是.NET裏面的特性(Attributes),它提供了添加到請求處理管道的額外方法。這裏使用Authorize過濾器能夠實現一樣的效果,不過代碼就顯然比以前更加簡潔優雅。測試
過濾器實現的機制:在MVC框架調用一個Action以前,它會檢查方法的定義中是否實現了特性(Attributes),若是實現的話,那麼在請求處理管道適當的位置,該特性定義的方法會被調用。ui
ActionFilterAttribute類既實現了IactionFilter接口,也實現IResultFilter接口。這是一個抽象類,它要求你必須提供一個實現。AuthorizeAttribute和HandleErrorAttribute類,則包含了一些有用的特性,而且能夠沒必要建立派生類進行使用。this
應用: 過濾器能夠被應用到控制器上也能夠用到Action方法上,應用到控制上時,表示全部的Action方法都有了這個過濾器,而且能夠混合使用,或屢次使用,以下所示:spa
1 [A] //表示全部的Action方法都會應用A過濾器 2 Public class DemoController:Controller 3 { 4 [B]//B,C過濾器只做用於此Action方法,但它也會有A過濾器的應用效果 5 [C] 6 Public ActionResult Index() 7 { 8 //操做部分... 9 return View(); 10 } 11 }
應用方式:特性的方式,如上面代碼所示。.net
執行順序:相同類型過濾器,執行順序靠近方法的先執行,不一樣類型的過濾器通常執行順序爲【authorize--->action--->actionResult】至於異常過濾器不分前後,只要拋出異常時就會執行異常過濾器。若是要調整執行順序,能夠經過調整Order方法值大小來控制執行順序,值越小,越先執行。下圖是Action/Result過濾器應用的執行順序圖
(1)、相同類型過濾器應用示例:兩個自定義Action過濾器MyFirstFilter,MyThreeFilter應用到同一個Action方法Index上。
Three控制器代碼以下:
MyFirstFilter 代碼以下:
MyThreeFilter代碼以下:
運行結果以下:
(2)、不一樣類型過濾器應用示例:有一個自定義Action過濾器MyFirstFilter,有一個自定義Result過濾器MySecondFilter,應用到同一個Action方法Index上。
Three控制器代碼以下:
MyFirstFilter 代碼以下:
MySecondFilter代碼以下:
運行結果以下:
看完上面的解釋,可能你如今對這些過濾器的執行順序,以及如何自定義過濾器還不明白,沒關係,下面咱們會逐一介紹這幾個基本的過濾器的使用,以及如何自定義過濾器。
全部實現了IAuthorizationFilter接口的均可以稱之爲受權過濾器:其定義以下:
1 public interface IAuthorizationFilter 2 { 3 void OnAuthorization(AuthorizationContext filterContext); 4 }
因爲MVC框架系統自帶的AuthorizeAttribute實現有一些突出的功能,而這種牽涉到安全的代碼必定要謹慎的編寫,因此通常咱們不會直接實現這個接口,而是去繼承AuthorizeAttribute這個類,並重寫其AuthorizeCore方法,簽名爲: bool AuthorizeCore(HttpContextBase httpContext) 而處理受權失敗的時候,能夠重寫其HandleUnauthorizedRequest方法,其簽名爲: void HandleUnauthorizedRequest(AuthorizationContext context) 。注意:驗證與受權是兩回事,驗證發生在受權以前。
默認的受權過濾器已經有了驗證的功能,其驗證的機理是利用Asp.net平臺自帶的驗證機制,如表單驗證和Windows驗證。除了驗證功能,它自己還有受權的功能。受權過濾器是全部過濾器中最先運行的。
通過Route到達了控制器的時候,在調用Action以前,MVC框架會檢測在相關的Action上是否有受權過濾器,若是有會調用OnAuthorization方法,若是此方法批准了請求,纔會調用相應的Action。
使用受權過濾器幾種狀況以下:
1.直接在Action上或者控制器上加Authorize,表示啓用了驗證,但不牽涉到受權。
2.添加Authorize(Users=「a,b」)],表示啓用了驗證,而且也啓用了受權,只有a或者b用戶能訪問此控制器。
3.當添加Authorize(Roles=「admin,Member」)]時的步驟以下:
---利用asp.net自帶的角色提供者,或者實現本身的角色提供者,實現本身的角色提供者時,只須要集成RoleProvider類型,並實現其中的全部方法或部分方法,最好實現全部方法。
---在Web程序的根目錄的Web.config文件中配置角色管理者。
---在適當的Action中利用Roles類型來訪問本身建立的RoleProvider中的相關方法。
使用內置的受權過濾器
MVC框架內置的受權過濾器AuthorizeAttribute,它容許咱們使用這個類的兩個公共屬性來指定受權策略,以下所示:
Users和Roles二者是而且的關係,例如Users=「a,b,c」,Roles=「admin」,表示用戶是a,b,c 其中一個而且是Admin角色才能訪問。
建立自定義的受權過濾器
方式一:直接實現IAuthorizationFilter接口,但不推薦這樣作,由於牽涉到安全方面的代碼。
方式二:繼承AuthorizeAttribute這個類,並重寫其AuthorizeCore方法,簽名爲: bool AuthorizeCore(HttpContextBase httpContext),代碼以下所示:
1 public class MyAuthorizeAttribute : AuthorizeAttribute 2 { 3 private string[] allowedUsers; 4 public MyAuthorizeAttribute(params string[] users) 5 { 6 allowedUsers = new string[] { "admin", "user1", "xf" }; 7 } 8 9 protected override bool AuthorizeCore(HttpContextBase httpContext) 10 { 11 return httpContext.Request.IsAuthenticated &&allowedUsers.Contains(httpContext.User.Identity.Name, 12 StringComparer.InvariantCultureIgnoreCase); 13 } 14 }
動做過濾器是能夠以用於任何目的的多用途過濾器,建立自定義動做過濾器須要實現IActionFilter接口,該接口代碼以下所示:
該接口定義了兩個方法,MVC框架在調用動做方法以前,會調用OnActionExecting方法。在調用動做方法以後,則會調用OnActionExecuted方法。
實現OnActionExecting方法
參數ActionExecutingContext對象繼承於ControllerContext,其中的2個屬性:
ActionDescriptor:提供了關於Action方法的相關信息
Result:類型爲ActionResult,經過給這個屬性設置一個非null的值就能夠取消這個請求。
咱們能夠用過濾器來取消一個請求,經過設置Result屬性便可。代碼以下所示:
1 public class MyActionFilterAttribute : FilterAttribute, IActionFilter 2 { 3 public void OnActionExecuting(ActionExecutingContext filterContext) 4 { 5 if(filterContext.HttpContext.Request.IsLocal) 6 { 7 filterContext.Result = new HttpNotFoundResult(); 8 } 9 } 10 public void OnActionExecuted(ActionExecutedContext filterContext) 11 { 12 //未作實現 13 } 14 }
這個例子經過用OnActionExecuting方法檢查請求是否來自本地機器,若是是,編隊用戶返回一個「404」未找到的響應。運行結果以下圖:
實現OnActionExecuted方法
咱們也能夠經過OnActionExecuted方法來執行一些跨越動做方法的任務,下面這個例子是計算動做方法運行的時間,代碼以下:
1 public class MyActionFilterAttribute : FilterAttribute, IActionFilter 2 { 3 private Stopwatch timer; 4 public void OnActionExecuting(ActionExecutingContext filterContext) 5 { 6 timer = Stopwatch.StartNew(); 7 } 8 public void OnActionExecuted(ActionExecutedContext filterContext) 9 { 10 timer.Stop(); 11 if (filterContext.Exception == null) 12 { 13 filterContext.HttpContext.Response.Write( 14 string.Format("動做方法延遲的時間: {0}", 15 timer.Elapsed.TotalSeconds)); 16 } 17 } 18 } 19 }
咱們將自定義的動做過濾器MyActionFilter應用到HomeController的Index方法上,運行結果以下:
結果過濾器是多用途的過濾器,他會對動做方法所產生結果進行操做,結果過濾器實現IResultFilter接口,建立自定義結果過濾器須要現IResultFilter接口,該接口代碼以下所示:
當結果過濾器運用於一個動做方法時,會在動做方法返回動做結果以前,調用OnResultExecuting方法,在返回動做結果以後,會調用OnResultExecuted方法。下面這個例子是計算動做方法返回結果運行的時間,代碼以下:
1 public class MyResultFilterAttribute : FilterAttribute, IResultFilter 2 { 3 private Stopwatch timer; 4 public void OnResultExecuting(ResultExecutingContext filterContext) 5 { 6 timer = Stopwatch.StartNew(); 7 } 8 public void OnResultExecuted(ResultExecutedContext filterContext) 9 { 10 timer.Stop(); 11 filterContext.HttpContext.Response.Write(string.Format("結果執行延遲時間: {0}", timer.Elapsed.TotalSeconds)); 12 } 13 }
咱們將自定義的結果過濾器MyResultFilter應用到HomeController的Index方法上,運行結果以下:
須要注意的是:動做過濾器是運行在頁面輸出以前,結果過濾器是運行在頁面輸出以後。
異常過濾器只有在調用一個動做方法而拋出未處理的異常纔會運行,這種異常來自如下位置:
A、另外一種過濾器(受權、動做、或結果過濾器)。
B、動做方法自己。
C、當動做結果被執行時。
使用內置的異常過濾器
HandleErrorAttribute(處理程序錯誤特性),它是MVC內嵌的異常過濾器,有如下3個重要的屬性:
1.ExceptionType:類型爲Type,表示但願被此過濾器處理的異常類型,包括其子類型,默認值爲System.Exception
2.View:類型爲string,表示此過濾器呈遞的視圖頁面,默認值爲Error
3.Master:呈遞的視圖頁的母板頁,若是不指定,視圖會用其默認的母版頁
內嵌的HandleErrorException只有在配置文件Web.config中配置的CustomError 的mode設置爲on的時候才生效(其默認模式爲RemoteOnly),以下圖所示:
此過濾器還會給視圖傳遞一個HandleErrorInfo類型的對象給視圖,以便視圖能夠顯示一些額外的關於錯誤的信息。下面是使用異常過濾器的示例。
應用到Index動做方法上:
在Views/Shared文件夾下添加一個顯示異常信息的視圖頁SpecialError.cshtml,頁面代碼以下:
1 @model HandleErrorInfo 2 <!DOCTYPE html> 3 <html> 4 <head> 5 <meta name="viewport" content="width=device-width" /> 6 <title>SpecialError</title> 7 </head> 8 <body> 9 <div> 10 <p> 11 There was a<b>@Model.Exception.GetType().Name</b> 12 while rendering<b>@Model.ControllerName</b>'s 13 <b>@Model.ActionName</b> action 14 </p> 15 </div> 16 </body> 17 </html>
運行結果以下:
建立自定義的異常過濾器
若是咱們對異常過濾器有特殊的需求,能夠經過自定義的異常過濾器來完成,建立自定義異常過濾器必須實現IExceptionFilter接口,該接口代碼以下:
當一個未知處理異常發生時,OnException方法會被調用。該方法的傳遞一個ExceptionContext對象,派生於ControllerContext類,定義了一些額外的過濾器專有屬性以下表所示:
拋出的異常經過Exception屬性是能夠訪問的。經過把ExceptionHandled屬性設置爲true,一個異常過濾器能夠報告它已經處理了該異常,應用於一個動做的全部異常過濾器都會被調用。
須要注意的是:若是一個動做方法的全部異常過濾器均爲把ExceptionHandled屬性設置爲true,MVC框架將使用默認的ASP.NET異常處理程序。
Result屬性有異常過濾器使用,以告訴MVC框架要作什麼,異常過濾器的兩個主要應用是記錄該異常到日誌,並把適當的消息顯示給用戶。下面的代碼將演示經過建立一個自定義的異常過濾器,當一個特定的鐘類的未處理異常出現時,把該用戶重定向到一個指定的錯誤頁面。
1 public class MyExectionAttribute:FilterAttribute,IExceptionFilter 2 { 3 public void OnException(ExceptionContext filterContext) 4 { 5 if(!filterContext.ExceptionHandled&& 6 filterContext.Exception is NullReferenceException) 7 { 8 filterContext.Result = new RedirectResult("~/Content/SpecialErrorPage.html"); 9 filterContext.ExceptionHandled = true; 10 } 11 } 12 }
而後在項目根目錄添加一個名爲Content的文件夾,在該文件夾下建立SpeciErrorPage.html文件,當異常被處理時,將以這個錯誤頁面顯示個用戶。該頁面代碼以下:
1 <!DOCTYPE html> 2 <html xmlns="http://www.w3.org/1999/xhtml"> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> 5 <title></title> 6 </head> 7 <body> 8 <h1>Sorry</h1> 9 <p>this is a Excetption test</p> 10 There was aNullReferenceException while renderingHome's Index action 11 </body> 12 </html>
在控制器中應用MyExection異常過濾器,並主動讓其拋出一個空引用異常,以便測試。
1 public class HomeController : Controller 2 { 3 [MyExection] 4 public ActionResult Index() 5 { 6 throw new NullReferenceException(); 7 } 8 }
運行結果以下:
總結:本文章簡單總結了對過濾器的理解以及如何使用MVC框架內置基本的過濾器和如何自定義過濾器及應用。