1、概括
MVC提供的幾種過濾器其實也是一種特性(Attribute),MVC支持的過濾器類型有四種,分別是:AuthorizationFilter(授權),ActionFilter(行為),ResultFilter(結果)和ExceptionFilter(異常),他們分別對應了四個篩選器接口IAuthorizationFilter、IActionFilter、IResultFilter和IExceptionFilter。這四種篩選器都有派生於一個公共的類FilterAttribute,該類指定了篩選器的執行順序和是否允許多個應用AllowedMultiple。這四種篩選器默認的執行順序為最先進行授權篩選,最后進行異常處理,中間則是ActionFilter和ResultedFilter。官網對FilterAttribute層次結構的介紹圖如下
2、MVC四大過濾器介紹
2.1、AuthorizeFilter篩選器
AuthorizeAttribute為最終授權過濾器的實現者,它實現了IAuthorizationFilter接口和FilterAttribute抽象類,接口中的OnAuthorization(AuthorizationContext filterContext)方法是最終驗證授權的邏輯(其中AuthorizationContext是繼承了ControllerContext類),AuthorizeCore方法是最終OnAuthorization()方法調用的最終邏輯。
- bool AuthorizeCore(HttpContextBase httpContext):授權驗證的邏輯處理,返回true則是通過授權,返回false則不是。若驗證不通過時,OnAuthorization方法內部會調用HandleUnauthorizedRequest
- void HandleUnauthorizedRequest(AuthorizationContext filterContext):這個方法是處理授權失敗的事情。
AuthorizeCore代碼如下:
protected virtual bool AuthorizeCore(HttpContextBase httpContext) { if (httpContext == null) { throw new ArgumentNullException("httpContext"); } IPrincipal user = httpContext.User; if (!user.Identity.IsAuthenticated) { return false; } if (_usersSplit.Length > 0 && !_usersSplit.Contains(user.Identity.Name, StringComparer.OrdinalIgnoreCase)) { return false; } if (_rolesSplit.Length > 0 && !_rolesSplit.Any(user.IsInRole)) { return false; } return true; }
我們不一定要用MVC默認的Authorize授權驗證規則,規則可以自己來定,自定義授權過濾器繼承IAuthorizeAttribute和FilterAttribute,由於OnAthurization()、AuthorizeCore()和HandleUnauthorizedRequest()方法都是虛方法,這些方法是可以重寫的,這樣就可以自定義自己的驗證規則和驗證失敗時的處理邏輯了。示例代碼如下
public class PermissionFilterAttribute: AuthorizationFilter { protected override bool AuthorizeCore(HttpContextBase httpContext) { return true; } protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { filterContext.Result=""; } }
授權過濾器的使用方式一如下:
[PermissionFilterAttribute]
public ActionResult index()
{
return View();
}
2.2、ActionFilter過濾器
ActionFilter過濾器是在Action方法執行前后會觸發,主要用於在Action執行前后處理一些相應的邏輯。ActionFilter的過濾器都繼承於ActionFilterAttribute抽象類,而它實現了IActionFilter、IResultFilter和FilterAttribute類,結構如下
因此自定義ActionFilter過濾器只要繼承ActionFilterAttribute,實現其中的方法即可。我們來舉一個簡單的例子,獲取Action方法的執行時長,代碼如下
public class DefaultController : Controller { [ActionExecTimeSpan] public ActionResult DoWork() { return View(); } } public class ActionExecTimeSpanAttribute : ActionFilterAttribute { private const string executeActionTimeKey = "ActionExecBegin"; public override void OnActionExecuting(ActionExecutingContext filterContext) { base.OnActionExecuting(filterContext); //記錄開始執行時間 filterContext.HttpContext.Items[executeActionTimeKey] = DateTime.Now; } public override void OnActionExecuted(ActionExecutedContext filterContext) { //計算執行時間,並記錄日志 if (filterContext.HttpContext.Items.Contains(executeActionTimeKey)) { DateTime endTime = DateTime.Now; DateTime beginTime = Convert.ToDateTime(filterContext.HttpContext.Items[executeActionTimeKey]); TimeSpan span = endTime - beginTime; double execTimeSpan = span.TotalMilliseconds; log.Info(execTimeSpan + "毫秒"); } // base.OnActionExecuted(filterContext); } }
2.3、ResultFilter過濾器
ResultFilter過濾器是對Action方法返回的Result結果進行執行時觸發的。它也分執行前和執行后兩個段執行,所有的ResultFilter都實現了IResultFilter接口和FilterAttribute類,看一下接口定義
public interface IResultFilter { void OnResultExecuting(ResultExecutingContext filterContext); void OnResultExecuted(ResultExecutedContext filterContext); }
其中OnResultExecuting和OnResultExecuted方法分別是在Result執行前、后(頁面展示內容生成前、后)觸發。使用ResultFilter篩選器最典型的應用就是頁面靜態化。
2.4、ExceptionFilter過濾器
該過濾器是在系統出現異常時觸發,可以對拋出的異常進行處理。所有的ExceptionFilter篩選器都是實現自IExceptionFilter接口
public interface IExceptionFilter { void OnException(ExceptionContext filterContext); }
實現OnException方法來實現對異常的自定義處理,MVC4中實現了默認的異常處理機制,源碼如下
public virtual void OnException(ExceptionContext filterContext) { if (filterContext == null) { throw new ArgumentNullException("filterContext"); } if (filterContext.IsChildAction) { return; } // If custom errors are disabled, we need to let the normal ASP.NET exception handler // execute so that the user can see useful debugging information. if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled) { return; } Exception exception = filterContext.Exception; // If this is not an HTTP 500 (for example, if somebody throws an HTTP 404 from an action method), // ignore it. if (new HttpException(null, exception).GetHttpCode() != 500) { return; } if (!ExceptionType.IsInstanceOfType(exception)) { return; } string controllerName = (string)filterContext.RouteData.Values["controller"]; string actionName = (string)filterContext.RouteData.Values["action"]; HandleErrorInfo model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName); filterContext.Result = new ViewResult { ViewName = View, MasterName = Master, ViewData = new ViewDataDictionary<HandleErrorInfo>(model), TempData = filterContext.Controller.TempData }; filterContext.ExceptionHandled = true; filterContext.HttpContext.Response.Clear(); filterContext.HttpContext.Response.StatusCode = 500; // Certain versions of IIS will sometimes use their own error page when // they detect a server error. Setting this property indicates that we // want it to try to render ASP.NET MVC's error page instead. filterContext.HttpContext.Response.TrySkipIisCustomErrors = true; }
Application_Start中將HandleErrorAttribute添加到全局過濾器GlobalFilterCollection中,系統即會對異常進行對應的處理。
3、其他的過濾器
3.1、OutputCache過濾器
表示一個特性,該特性用於標記將緩存其輸出的操作方法。當用戶訪問頁面時,整個頁面將會被服務器保存在內存中,這樣就對頁面進行了緩存。當用戶再次訪問該頁,
頁面不會再次執行數據操作,頁面首先會檢查服務器中是否存在緩存,如果緩存存在,則直接從緩存中獲取頁面信息,如果頁面不存在,則創建緩存。OutputCache的代碼定義片段如下:
[AttributeUsageAttribute(AttributeTargets.Class|AttributeTargets.Method, Inherited = true, AllowMultiple = false)] public class OutputCacheAttribute : ActionFilterAttribute, IExceptionFilter
從上面的代碼中可以看到該特性可以應用在類,方法上面。在mvc中,就可以直接在控制器上面或者控制器中的Action上面直接使用,做到細粒度的對緩存的控制。
namespace OutputCacheDemo.Controllers { [OutputCache(Duration = 10)] public class HomeController : Controller { // GET: Home public string Index() { return DateTime.Now.ToString(); } } }
上面的代碼是將OutputCache特性標記在了控制器類上,以達到該控制器上所有的Action都將應用該特性,過期時間設置為10s。10s后緩存過期,再訪問就會更新時間。OutputCache特性也可以設置在Action方法上面,以達到更細粒度的控制緩存,代碼如下:
public class HomeController : Controller { [OutputCache(Duration = 10)] // GET: Home public string Index() { return DateTime.Now.ToString(); } }
此時,只有Index的頁面進行了緩存。如果多個控制器或者Action使用相同的緩存配置,可以在配置文件中進行統一配置。
<system.web> <caching> <outputCacheSettings> <outputCacheProfiles > <add name='myoutputcache' duration='10'/> </outputCacheProfiles> </outputCacheSettings> </caching> <compilation debug="true" targetFramework="4.5"/> <httpRuntime targetFramework="4.5"/> </system.web>
應用名稱為myoutputcache的緩存代碼如下:
public class HomeController : Controller { [OutputCache(CacheProfile = "myoutputcache")] // GET: Home public string Index() { return DateTime.Now.ToString(); } }
注意:當控制器和Action同時使用了OutputCache特性時,以Action為主。