MVC四大筛选器—ActionFilter&ResultedFilter

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筛选器最典型的应用就是页面静态化,我们以后在其他文章中在对此进行详细讲解

学习什么时候都不晚,从现在起我们一起

 

posted @ 2018-08-20 23:04  Mr▪King  阅读(286)  评论(0编辑  收藏  举报