您好,登錄后才能下訂單哦!
Filter在請求管道注入額外的邏輯。他們提供簡單優雅的方法實現橫切點關注。這個術語指的是在穿越整個應用程序中使用,而且不適合使用在任何單獨的地方,所以這會打破關注模式的分離。經典的橫切點關注的例子比如日志,認證,緩存。
Filter也被認為是橫切點關注,因為這個術語在其他web application框架,包括Ruby也是實現同樣功能。然而MVC Framework Filter完全不同于ASP.NET 平臺的Request.Filter和Response.Filter對象。Request.Filter和Response.Filter對象在request和response流上執行轉換。你可以在MVC application中使用Request.Filter 和Response.Filter,但是,通常當ASP.NET MVC程序員談到filter時,指的是接下要談的類型,在本文,我們會展示MVC Framework提供的filter不同的之類,如何創建使用filter,如何控制他們的執行。
使用Filter
我們想要讓action方法只能由認證過的用戶使用。我們可以在每個action方法檢查請求的認證狀態。如下面的代碼,顯式的在action方法檢測認證信息。
namespace SportsStore.WebUI.Controllers {
public class AdminController : Controller {
// ... instance variables and constructor
public ViewResult Index() {
if (!Request.IsAuthenticated) {
FormsAuthentication.RedirectToLoginPage();
}
// ...rest of action method
}
public ViewResult Create() {
if (!Request.IsAuthenticated) {
FormsAuthentication.RedirectToLoginPage();
}
// ...rest of action method
}
public ViewResult Edit(int productId) {
if (!Request.IsAuthenticated) {
FormsAuthentication.RedirectToLoginPage();
}
// ...rest of action method
}
// ... other action methods
}
}
可以看到這方法中有很多重復代碼,所以我們決定使用filter。如下代碼:
namespace SportsStore.WebUI.Controllers {
[Authorize]
public class AdminController : Controller {
// ... instance variables and constructor
public ViewResult Index() {
// ...rest of action method
}
public ViewResult Create() {
// ...rest of action method
}
public ViewResult Edit(int productId) {
// ...rest of action method
}
// ... other action methods
}
}
Filter是.NET特性,它是加在request處理管道上的額外步驟。上例中,我們使用Authorize filter,和之前那種重復檢查的方法,有相同的效果。
特性是從System.Attribute繼承的特殊的.NET類。你可以把它們附加到其它代碼上,包括類,方法,屬性,字段。目的就是在編譯的代碼中植入額外的信息,在運行事情你就可以讀回它們。
C#中,特性使用的時候要加上方括號,你能通過命名參數語法組合它們的公共屬性(比如,[MyAttribute(SomeProperty=value)])。在C#編譯器命名約定中,如果特性類以單詞Attribute結尾,你可以忽略這部分(比如,應用AuthorizeAttribute時,可以直接寫[Authorize])
Filter的四種基本類型
MVC Framework 支持四種不同的filter類型。每個都允許你在請求管道的不同點處引入自己的邏輯,下表列出了這四種類型。
在MVC Framework調用action之前,它會檢查方法定義,查看實現了上述表格中的接口的特性是否存在。如果存在,那么在request管道的恰當處,有這些接口定義的方法就會被調用。framework包括了實現filter接口的默認的特性類。
注意ActionFilterAttribute類實現了IActionFilter和IResultFilter 接口。此類是抽象類,迫使你提供一個實現。其它類,AuthorizeAttribute和HandleErrorAttribute包含了有用的功能,可以不需要繼承直接使用。
在controller和action方法上應用Filter
Filter可以應用在單個action方法上,或者整個controller上,在上面的例子,我們把Authorize filter應用于 AdminController類,這和應用在每一個controller中的action方法有同樣的效果,如下:
namespace SportsStore.WebUI.Controllers {
public class AdminController : Controller {
// ... instance variables and constructor
[Authorize]
public ViewResult Index() {
if (!Request.IsAuthenticated) {
FormsAuthentication.RedirectToLoginPage();
}
// ...rest of action method
}
[Authorize]
public ViewResult Create() {
if (!Request.IsAuthenticated) {
FormsAuthentication.RedirectToLoginPage();
}
// ...rest of action method
}
// ... other action methods
}
}
你可以應用多個filter,無論他們匹配到controller或者單個action方法。下面展示3種不同的使用filter的方法。
[Authorize(Roles="trader")] // applies to all actions
public class ExampleController : Controller {
[ShowMessage] // applies to just this action
[OutputCache(Duration=60)] // applies to just this action
public ActionResult Index() {
// ... action method body
}
}
注意,如果你為controller自定義了基類,那么任何應用在基類上的filter也會應用在子類上。
使用Authorization Filter
Authorization filter 是最先運行的filter,它在其他filter和action方法之前被調用,就如它的命名,這些filter強迫你的認證策略,確保action方法只能被認證過的用戶調用。Authorization filter 實現IAuthorizationFilter 接口,如下代碼:
namespace System.Web.Mvc {
public interface IAuthorizationFilter {
void OnAuthorization(AuthorizationContext filterContext);
}
}
MVC Framework從瀏覽器收到一個請求,路由系統已經處理了請求的URL,抽取了controller和action的名字。一個controller的實例創建,但是在action方法調用之前,MVC Framework檢查是否有任何authorization filter應用在action方法。如果有,那么這個由IAuthorizationFilter接口定義的方法——OnAuthorization方法會被調用,如果authentication filter同意了請求,那么處理管道中下一步才會開始,否則請求就會拒絕。
創建Authentication Filter
理解authentication filter工作原理的最好方法就是創建一個。下面的代碼是一段簡單的示例。它僅僅檢查用戶是否登錄了。(Request.IsAuthenticated是ture),同時username要出現在允許用戶列表中。
using System;
using System.Linq;
using System.Web.Mvc;
using System.Web;
namespace MvcFilters.Infrastructure.Filters {
public class CustomAuthAttribute : AuthorizeAttribute {
private string[] allowedUsers;
public CustomAuthAttribute(params string[] users) {
allowedUsers = users;
}
protected override bool AuthorizeCore(HttpContextBase httpContext) {
return httpContext.Request.IsAuthenticated &&
allowedUsers.Contains(httpContext.User.Identity.Name,
StringComparer.InvariantCultureIgnoreCase);
}
}
}
這個創建了一個authorization filter最簡單的方法是繼承AuthorizeAttribute類,重寫AuthorizeCore方法。這確保了從AuthorizeAttribute內建功能中獲益。
警告:編寫安全保障代碼是危險行為
我們寫了一個自定義authorization filter的例子,因為我們認為它可以展示filter的運行方式,但是對于編寫自己安全代碼,我們要小心。這種技能很少有人會去使用,這會讓我們的應用程序安全出現未考慮到的漏洞。
我們盡可能的使用那些廣泛使用,并且被實踐過的安全代碼,在這種情況下,MVC Framework提供了一個功能全面的authorization filter,可以通過繼承它來實現自定義認證策略。我們盡可能使用它,并且我們建議你也這樣做,這樣,當你的安全數據在網上泄露了,你至少可以罵微軟。
我們的filter構造方法帶有一組名字的數組,這數組內容是被認證的用戶的信息。我們的filter包含一個PerformAuthenticationCheck方法,確保請求是認證的,用戶也是在已認證的集合中。這個類中有趣的部分是OnAuthorization方法的實現。傳給這個方法的參數是AuthorizationContext 類的一個實例,AuthorizationContext是繼承于ControllerContext的。ControllerContext 能使我們訪問一些有用的對象,不僅僅是HttpContextBase(通過HttpContextBase,我們可以訪問request的細節)。ControllerContext的屬性在下表中列出,所以這些用于不同的action filer的context 對象都繼承自這個類,所以你可以始終如一的使用這些屬性。
當我們想檢查是否請求被認證了,我們就如下的寫法:
... filterContext.HttpContext.Request.IsAuthenticated ...
AuthorizationContext定義了2個額外的屬性,如下表:
第一個屬性ActionDescriptor,返回一個System.Web.Mvc.ActionDescriptor實例,你可以通過這個屬性得到你應用了filter的action的信息。第二個屬性,Result,是讓你filter工作的關鍵。如果你要對一個action方法認證請求,那么你在OnAuthorization方法上不用做什么,MVC Framework認為這個請求應該處理。但是,如果你設置context對象的Result屬性為一個ActionResult對象,MVC
Framework將使用使用它作為整個請求的結果。管道中剩下的步驟則不會被運行了,你提供的result會運行輸出給用戶。
在我們的例子中,如果我們的PerformAuthenticationCheck返回false(表面整個請求沒有認證通過,或者用戶不是認證用戶),然后我們創建一個HttpUnauthorizedResultaction result,然后制定context的 Result 屬性,如下:
filterContext.Result = new HttpUnauthorizedResult();
要使用我們的自定義authorization filter。我們只需要將特性放在action方法上,如下代碼:
...
[CustomAuth("adam", "steve", "bob")]
public ActionResult Index() {
return View();
}
...
使用內建的認證Filter
MVCFramework包含了一個實用的認證filter,AuthorizeAttribute。我們可以使用兩個公共屬性指定認證策略,如下:
...
[Authorize(Users="adam, steve, bob", Roles="admin")]
public ActionResult Index() {
return View();
}
...
上例中,我們指定了用戶和權限。這意味著只有當用戶和權限同時滿足的情況下才認證系統才會通過。這里還有一個隱式的條件,就是這個請求必須是被認證的。如果我們不指定任何用戶和權限,那么任何用戶都可使用此action方法。
對大多數應用程序來說,AuthorizeAttribute提供的認證策略已經足夠了。如果你需要實現一些特殊的功能,你可以繼承這個類。直接實現IAuthorizationFilter接口風險比較小,但是你應該仔細思考,你的策慮所帶來的影響,而且要徹底的測試。
AuthorizeAttribute類提供2個不同方法。
AuthorizeCore方法,由AuthorizeAttribute的OnAuthorization調用,實現認證檢查。
HandleUnauthorizedRequest 方法,當認證檢查失敗的時候調用。
實現自定義認證失敗策略
默認的處理認證失敗的策略是定向到用戶登錄頁面。我們不總希望如此。比如,如果使用ajax,發送重定向肯能會讓用戶在頁面中間看到一個登錄頁面。幸運的是,我們可以重寫AuthorizeAttribute類的HandleUnauthorizedRequest方法來創建一個自定義策略。
下面的例子實現了自定義認證失敗的策略:
using System.Web.Mvc;
namespace MvcFilters.Infrastructure.Filters {
public class AjaxAuthorizeAttribute : AuthorizeAttribute {
protected override void HandleUnauthorizedRequest(AuthorizationContext context) {
if (context.HttpContext.Request.IsAjaxRequest()) {
UrlHelper urlHelper = new UrlHelper(context.RequestContext);
context.Result = new JsonResult {
Data = new {
Error = "NotAuthorized",
LogOnUrl = urlHelper.Action("LogOn", "Account")
}, JsonRequestBehavior = JsonRequestBehavior.AllowGet};
} else {
base.HandleUnauthorizedRequest(context);
}
}
}
}
當Filter發現是ajax請求,就會按照JSON數據的方式響應。正常的請求還是由基類中默認的策略處理,ajax客戶端必須也要寫響應的代碼響應。
使用Exception Filters
Exception filters只有在運行action方法拋出異常的時候才會運行。這個異常可能從以下幾個地方拋出:
其他類型的filter(認證,action,result filter)
action方法自身
當action結果運行后。
創建Exception Filter
Exception filters必須實現IExceptionFilter接口,接口如下:
namespace System.Web.Mvc {
public interface IExceptionFilter {
void OnException(ExceptionContext filterContext);
}
}
OnException方法在拋出異常的時候調用。參數是ExceptionContext。這個類和認證filter的參數類似,也是繼承自ControllerContext類。所以你可以獲得request信息,并且定義一下額外的指定的filter屬性,這些屬性如下表:
exception filter的Result屬性是告訴MVC Framework要做什么。兩個主要的用處是記錄異常和顯示恰當的消息給用戶。下例顯示了一個演示,當特殊的未處理異常發生時,就會跳轉到一個指定的錯誤頁面
using System.Web.Mvc;
using System;
namespace MvcFilters.Infrastructure.Filters {
public class MyExceptionAttribute: FilterAttribute, IExceptionFilter {
public void OnException(ExceptionContext filterContext) {
if (!filterContext.ExceptionHandled &&
filterContext.Exception is NullReferenceException) {
filterContext.Result = new RedirectResult("/SpecialErrorPage.html");
filterContext.ExceptionHandled = true;
}
}
}
}
filter回應NullReferenceException實例,而且僅當沒有其他exception filter指示處理了異常才發生。我們把用戶重定向到一個錯誤頁面,如下代碼
...
[MyException]
public ActionResult Index() {
...
如果Index action方法拋出異常,是一個NullReferenceException,而且沒有其他 exception filter處理這一個異常,那么我們的filter將重定向到SpecialErrorPage.html。
使用內建的Exception Filter
HandleErrorAttribute是 IExceptionFilter 接口一個實現,它使得創建exception filter更簡單。你可以通過下面的屬性指定異常和view和layout的名字,從而指定一個異常,如下所示:
當一個未處理的類型異常出現是,filter會把HTTP result code設置為500,意思是服務器錯誤,同時顯示用戶通過View屬性指定的view(通過Master使用layout),下面的例子展示了如何使用HandleErrorAttribute filter
[HandleError(ExceptionType=typeof(NullReferenceException), View="SpecialError")]
public ActionResult Index() {
...
上述例子,我們對NullReferenceException類型感興趣,希望出現異常時顯示SpecialErrorView
注意,The HandleErrorAttribute filter 僅在Web.config中自定義error是enabled的情況下工作。比如,在<system.web>節點增加節點 <customErrors mode="On" />。默認的自定義error模式是RemoteOnly,意味著在開發期間,HandleErrorAttribute將不攔截異常,但是當你部署產品服務器,并且從其他電腦上獲得請求,HandleErrorAttribute就會采取行動了,要想了解終端用戶所看到的,必須確保把自定義error mode設置為On。
當呈現一個view時,HandleErrorAttribute filter傳遞一個HandleErrorInfo view model對象,你可以在消息中包含異常的細節,展示給用戶。如下例子,當現實錯誤信息時,使用View Model對象:
@Model HandleErrorInfo
@{
ViewBag.Title = "Sorry, there was a problem!";
}
<p>
There was a <b>@Model.Exception.GetType().Name</b>
while rendering <b>@Model.ControllerName</b>'s
<b>@Model.ActionName</b> action.
</p>
<p>
The exception message is: <b><@Model.Exception.Message></b>
</p>
<p>Stack trace:</p>
<pre>@Model.Exception.StackTrace</pre>
效果圖如下:
使用Action和Result Filter
Action和Result Filter是常用的filter,可以用于任何目的。它們都遵循一個公共模式。內建的類創建filter,IActionFilter的類型,實現全部的接口。接口如下:
namespace System.Web.Mvc {
public interface IActionFilter {
void OnActionExecuting(ActionExecutingContext filterContext);
void OnActionExecuted(ActionExecutedContext filterContext);
}
}
此接口定義了2個方法,MVC Framework 在action方法調用之前,先調用OnActionExecutin方法。在action方法調用之后,調用OnActionExecuted方法。
實現OnActionExecuting方法
The OnActionExecuting 方法在action方法在action方法調用之前先調用。你可以通過這個來檢查請求,選擇取消請求,修改請求,或者啟動另一個活動跨越action的調用。此方法的參數是一個ActionExecutingContext ,是ControllerContext的子類,有2個同樣的屬性,和你在其他context對象中看到的一樣。詳細描述如下表:
你可以有選擇的通過設置parameter的result屬性來取消請求,如下演示代碼:
namespace MvcFilters.Infrastructure.Filters {
public class MyActionFilterAttribute : FilterAttribute, IActionFilter {
public void OnActionExecuting(ActionExecutingContext filterContext) {
if (!filterContext.HttpContext.Request.IsSecureConnection) {
filterContext.Result = new HttpNotFoundResult();
}
}
public void OnActionExecuted(ActionExecutedContext filterContext) {
// do nothing
}
}
}
此例中,使用OnActionExecuting方法檢查請求是否是SSL的,如果不是,返回404-Not Found響應給用戶。
注意,如上例所示,你不需要將IActionFilter接口中的方法都實現,如果你不需要加任何邏輯,就置空。消息不要拋出NotImplementedException異常,因為如果你這樣做,exception filter就會被執行。
實現OnActionExecuted方法
你也可以使用filter去執行一些任務,跨越執行action方法。下面是一個簡單的例子計算action方法執行的時間開銷。
using System.Diagnostics;
using System.Web.Mvc;
namespace MvcFilters.Infrastructure.Filters {
public class ProfileAttribute : FilterAttribute, IActionFilter {
private Stopwatch timer;
public void OnActionExecuting(ActionExecutingContext filterContext) {
timer = Stopwatch.StartNew();
}
public void OnActionExecuted(ActionExecutedContext filterContext) {
timer.Stop();
if (filterContext.Exception == null) {
filterContext.HttpContext.Response.Write(
string.Format("Action method elapsed time: {0}",
timer.Elapsed.TotalSeconds));
}
}
}
}
此例中,我們使用OnActionExecuting方法,啟動一個計數器。當action方法完成后,OnActionExecuted方法調用。然后我們停止計時器,寫出響應,輸出所花的時間。結果圖如下:
傳遞給OnActionExecuted方法的參數是一個ActionExecutedContext對象。此類定義了一些額外的屬性,如下表。Exception屬性返回任何action方法拋出的異常,ExceptionHandled屬性指示了是否有其他filter處理了它。
屬性Canceled,如果其他filter cancel了一個請求(通過設置result屬性的值),那么返回true。但我們的OnActionExecuted方法仍然調用,只有這樣我們才能是否使用的資源。
實現Result Filter
Action filters和result filters有一些共同點. Result filters對于action results 就好比action filters
對于action methods. Result filters 實現IResultFilter 接口, 接口代碼如下:
namespace System.Web.Mvc {
public interface IResultFilter {
void OnResultExecuting(ResultExecutingContext filterContext);
void OnResultExecuted(ResultExecutedContext filterContext);
}
}
之前,我們說過action方法如何返回action result。這運行我們分離action的意圖和執行。當我們應用一個result filter到action方法上,一旦action方法返回action result,OnResultExecuting方法就被調用,但是在action result執行之前 。OnResultExecuted方法在action result執行后調用。這些方法的參數分別是ResultExecutingContext 和ResultExecutedContext 對象,它們和action filter的那部分非常相似。它們有相同的屬性,相同的效果。下例代碼展示了一個簡單的 result filter的例子。
using System.Diagnostics;
using System.Web.Mvc;
namespace MvcFilters.Infrastructure.Filters {
public class ProfileResultAttribute : FilterAttribute, IResultFilter {
private Stopwatch timer;
public void OnResultExecuting(ResultExecutingContext filterContext) {
timer = Stopwatch.StartNew();
}
public void OnResultExecuted(ResultExecutedContext filterContext) {
timer.Stop();
filterContext.HttpContext.Response.Write(
string.Format("Result execution - elapsed time: {0}",
timer.Elapsed.TotalSeconds));
}
}
}
這個filter計算了執行result所需要的時間。把這個filter附加的action方法上。
...
[ProfileResult]
public ActionResult Index() {
return View();
}
...
現在我們轉到action方法,輸出的內容如下。
注意,從filter中獲得的性能信息出現在頁面底部。這是因為我們在 action result 執行后——也就是view已經呈現了,才寫下我們的消息。我們先前的filte在action result執行之前寫到response的,因此它出現在page的頂部。
使用內建的Action和Result Filter類
MVC Framework 包含了一個內建的類,可以創建action 和result filters.
但是不像內建的authorization 和exception filter, 它不提供任何有用的細節. 這個類是ActionFilterAttribute 代碼如下:
public abstract class ActionFilterAttribute : FilterAttribute, IActionFilter, IResultFilter{
public virtual void OnActionExecuting(ActionExecutingContext filterContext) {
}
public virtual void OnActionExecuted(ActionExecutedContext filterContext) {
}
public virtual void OnResultExecuting(ResultExecutingContext filterContext) {
}
public virtual void OnResultExecuted(ResultExecutedContext filterContext) {
}
}
}
使用這個類的好處就是你不需要實現你用不到的方法。如下例子,展示了繼承ActionFilterAttribute的filter,使用action方法和action result執行,合并了我們的性能測量。
using System.Diagnostics;
using System.Web.Mvc;
namespace MvcFilters.Infrastructure.Filters {
public class ProfileAllAttribute : ActionFilterAttribute {
private Stopwatch timer;
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
timer = Stopwatch.StartNew();
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
timer.Stop();
filterContext.HttpContext.Response.Write(
string.Format("Action method elapsed time: {0}",
timer.Elapsed.TotalSeconds));
}
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
timer = Stopwatch.StartNew();
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
timer.Stop();
filterContext.HttpContext.Response.Write(
string.Format("Action result elapsed time: {0}",
timer.Elapsed.TotalSeconds));
}
}
}
ActionFilterAttribute 類實現了IActionFilter 和IResultFilter 接口,意味著MVC Framework把這個繼承類看作兩種filter類型, 即使并不是所有的方法都被重寫。如果我們在上述的例子應用這個filter到action方法,我們的輸出就如下:
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。