MVC四大篩選器—ActionFilter&ResultedFilter ExceptionFilter

AuthorizeFilter篩選器

 

在Action的執行中包括兩個重要的部分,一個是Action方法本身邏輯代碼的執行,第二個就是Action方法的篩選器的執行。

MVC4中篩選器都是以AOP(面向方面編程)的方式來設計的,通過對Action方法上標註相應的Attribute標籤來實現。MVC4提供了四種篩選器,分別爲:AuthorizationFilter、ActionFilter、ExceptionFilter和ResultFilter,他們分別對應了四個篩選器接口IAuthorizationFilter、IActionFilter、IExceptionFilter和IResultFilter。

這四種篩選器都有派生於一個公共的類FilterAttribute,該類指定了篩選器的執行順序Order和是否允許多個應用AllowedMultiple。這四種篩選器默認的執行順序爲最先進行授權篩選,最後進行異常處理,中間則是ActionFilter和ResultedFilter。

下面是抽象類FilterAttribute的類圖

 

下面我們來具體列舉一下各個篩選器的作用和實現

從字面上我們就能看出這是對Controller或Action方法授權的篩選器,即在Controller或Action方法執行前,首先會先執行該篩選器,若通過,纔會繼續執行。下面是此篩選器的簡單類圖

 

AuthorizeAttribute爲最終授權篩選器的實現者,它實現了IAuthorizationFilter接口和FilterAttribute抽象類,接口中的OnAuthorization(AuthorizationContext filterContext)方法是最終驗證授權的邏輯(其中AuthorizationContext是繼承了ControllerContext類)

 

  1.  
    protected virtual bool AuthorizeCore(HttpContextBase httpContext)
  2.  
    {
  3.  
    if (httpContext == null)
  4.  
    {
  5.  
    throw new ArgumentNullException("httpContext");
  6.  
    }
  7.  
     
  8.  
    IPrincipal user = httpContext.User;
  9.  
    if (!user.Identity.IsAuthenticated)
  10.  
    {
  11.  
    return false;
  12.  
    }
  13.  
     
  14.  
    if (_usersSplit.Length > 0 && !_usersSplit.Contains(user.Identity.Name, StringComparer.OrdinalIgnoreCase))
  15.  
    {
  16.  
    return false;
  17.  
    }
  18.  
     
  19.  
    if (_rolesSplit.Length > 0 && !_rolesSplit.Any(user.IsInRole))
  20.  
    {
  21.  
    return false;
  22.  
    }
  23.  
     
  24.  
    return true;
  25.  
    }

AuthorizeCore方法是最終OnAuthorization()方法調用的最終邏輯,從代碼可以看出,當同時指定了users和roles時,兩者只有同時滿足條件時纔可以驗證授權通過。如

  1.  
    [Authorize(Users="zhangsan", Roles="Admin")]
  2.  
    public ActionResult ActionMethod()
  3.  
    {
  4.  
    }

 

則只有用戶zhangsan,且用戶屬於Admin角色時才能驗證授權通過。

若驗證不通過時,OnAuthorization方法內部會調用HandleUnauthorizedRequest

虛方法進行處理,代碼如下:

  1.  
    protected virtual void HandleUnauthorizedRequest(AuthorizationContext filterContext)
  2.  
    {
  3.  
    // Returns HTTP 401 - see comment in HttpUnauthorizedResult.cs.
  4.  
    filterContext.Result = new HttpUnauthorizedResult();
  5.  
    }

 

該方法設置了參數上下文中ActionResult的值,用於供View展示。

我們可以自定義Authorize篩選器,由於OnAthurization()、AuthorizeCore()和HandleUnauthorizedRequest()方法都是虛方法,我們自定義的Authorize篩選器只需要繼承AuthorizeAttribute類,重寫以上三種方法,這樣就可以自定義自己的驗證規則和驗證失敗時的處理邏輯了。

 

IAuthorizationFilter還有其他類型的實現類,如RequireHttpsAttribute、ValidateInputAttribute都是實現了OnAuthorization()方法,來完成各自篩選器處理的。

 

ExceptionFilter過濾器


 

該篩選器是在系統出現異常時觸發,可以對拋出的異常進行處理。所有的ExceptionFilter篩選器都是實現自IExceptionFilter接口  

 

  1.  
    public interface IExceptionFilter
  2.  
    {
  3.  
    void OnException(ExceptionContext filterContext);
  4.  
    }

實現OnException方法來實現對異常的自定義處理

MVC4中實現了默認的異常處理機制,源碼如下 

  1.  
    public virtual void OnException(ExceptionContext filterContext)
  2.  
    {
  3.  
    if (filterContext == null)
  4.  
    {
  5.  
    throw new ArgumentNullException("filterContext");
  6.  
    }
  7.  
    if (filterContext.IsChildAction)
  8.  
    {
  9.  
    return;
  10.  
    }
  11.  
     
  12.  
    // If custom errors are disabled, we need to let the normal ASP.NET exception handler
  13.  
    // execute so that the user can see useful debugging information.
  14.  
    if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled)
  15.  
    {
  16.  
    return;
  17.  
    }
  18.  
     
  19.  
    Exception exception = filterContext.Exception;
  20.  
     
  21.  
    // If this is not an HTTP 500 (for example, if somebody throws an HTTP 404 from an action method),
  22.  
    // ignore it.
  23.  
    if (new HttpException(null, exception).GetHttpCode() != 500)
  24.  
    {
  25.  
    return;
  26.  
    }
  27.  
     
  28.  
    if (!ExceptionType.IsInstanceOfType(exception))
  29.  
    {
  30.  
    return;
  31.  
    }
  32.  
     
  33.  
    string controllerName = (string)filterContext.RouteData.Values["controller"];
  34.  
    string actionName = (string)filterContext.RouteData.Values["action"];
  35.  
    HandleErrorInfo model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);
  36.  
    filterContext.Result = new ViewResult
  37.  
    {
  38.  
    ViewName = View,
  39.  
    MasterName = Master,
  40.  
    ViewData = new ViewDataDictionary<HandleErrorInfo>(model),
  41.  
    TempData = filterContext.Controller.TempData
  42.  
    };
  43.  
    filterContext.ExceptionHandled = true;
  44.  
    filterContext.HttpContext.Response.Clear();
  45.  
    filterContext.HttpContext.Response.StatusCode = 500;
  46.  
     
  47.  
    // Certain versions of IIS will sometimes use their own error page when
  48.  
    // they detect a server error. Setting this property indicates that we
  49.  
    // want it to try to render ASP.NET MVC's error page instead.
  50.  
    filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
  51.  
    }

Application_Start中將HandleErrorAttribute添加到全局篩選器GlobalFilterCollection中,系統即會對異常進行對應的處理。
我們現在實現一個自定義的異常處理篩選器,在處理完後記錄異常信息至日誌文件中  
  1.  
    public class MyExceptionHandleAttribute : HandleErrorAttribute
  2.  
    {
  3.  
    public MyExceptionHandleAttribute()
  4.  
    : base()
  5.  
    {
  6.  
    }
  7.  
     
  8.  
    public void OnException(ExceptionContext filterContext)
  9.  
    {
  10.  
    base.OnException(filterContext);
  11.  
    //記錄日誌
  12.  
    log.Info(filterContext.Exception);
  13.  
    }
  14.  
    }

在GlobalFilterCollection添加MyExceptionHandleAttribute 即可使用自定義的異常篩選器來處理

ActionFilter篩選器

ActionFilter篩選器是在Action方法執行前後會觸發,主要用於在Action執行前後處理一些相應的邏輯。ActionFilter的篩選器都繼承於ActionFilterAttribute抽象類,而它實現了IActionFilter、IResultFilter和FilterAttribute類,結構如下

 

因此自定義ActionFilter篩選器只要繼承ActionFilterAttribute,實現其中的方法即可。

我們來舉一個簡單的例子,獲取Action方法的執行時長,代碼如下

 

  1.  
    public class DefaultController : Controller
  2.  
    {
  3.  
    [ActionExecTimeSpan]
  4.  
    public ActionResult DoWork()
  5.  
    {
  6.  
    return View();
  7.  
    }
  8.  
    }
  9.  
     
  10.  
    public class ActionExecTimeSpanAttribute : ActionFilterAttribute
  11.  
    {
  12.  
    private const string executeActionTimeKey = "ActionExecBegin";
  13.  
     
  14.  
    public override void OnActionExecuting(ActionExecutingContext filterContext)
  15.  
    {
  16.  
    base.OnActionExecuting(filterContext);
  17.  
    //記錄開始執行時間
  18.  
    filterContext.HttpContext.Items[executeActionTimeKey] = DateTime.Now;
  19.  
    }
  20.  
     
  21.  
    public override void OnActionExecuted(ActionExecutedContext filterContext)
  22.  
    {
  23.  
    //計算執行時間,並記錄日誌
  24.  
    if (filterContext.HttpContext.Items.Contains(executeActionTimeKey))
  25.  
    {
  26.  
    DateTime endTime = DateTime.Now;
  27.  
    DateTime beginTime = Convert.ToDateTime(filterContext.HttpContext.Items[executeActionTimeKey]);
  28.  
    TimeSpan span = endTime - beginTime;
  29.  
    double execTimeSpan = span.TotalMilliseconds;
  30.  
    log.Info(execTimeSpan + "毫秒");
  31.  
    }
  32.  
    //
  33.  
    base.OnActionExecuted(filterContext);
  34.  
    }
  35.  
    }

 

ResultFilter篩選器

 

ResultFilter篩選器是對Action方法返回的Result結果進行執行時觸發的。它也分執行前和執行後兩個段執行

所有的ResultFilter都實現了IResultFilter接口和FilterAttribute類,看一下接口定義

 

  1.  
    public interface IResultFilter
  2.  
    {
  3.  
    void OnResultExecuting(ResultExecutingContext filterContext);
  4.  
     
  5.  
    void OnResultExecuted(ResultExecutedContext filterContext);
  6.  
    }

 

其中OnResultExecuting和OnResultExecuted方法分別是在Result執行前、後(頁面展示內容生成前、後)觸發。
使用ResultFilter篩選器最典型的應用就是頁面靜態化,我們以後在其他文章中在對此進行詳細講解

學習什麼時候都不晚,從現在起我們一起

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章