zoukankan      html  css  js  c++  java
  • MVC过滤器特性

    一、概述

    MVC提供的几种过滤器其实也是一种特性(Attribute),MVC支持的过滤器类型有四种,分别是:AuthorizationFilter(授权),ActionFilter(行为),ResultFilter(结果)和ExceptionFilter(异常),他们分别对应了四个筛选器接口IAuthorizationFilter、IActionFilter、IResultFilter和IExceptionFilter。这四种筛选器都有派生于一个公共的类FilterAttribute,该类指定了筛选器的执行顺序和是否允许多个应用AllowedMultiple。这四种筛选器默认的执行顺序为最先进行授权筛选,最后进行异常处理,中间则是ActionFilter和ResultedFilter。官网对FilterAttribute层次结构的介绍图如下

    二、MVC四大过滤器

    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、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);
            }
        }

    3、ResultFilter过滤器

    ResultFilter过滤器是对Action方法返回的Result结果进行执行时触发的。它也分执行前和执行后两个段执行,所有的ResultFilter都实现了IResultFilter接口和FilterAttribute类,看一下接口定义

     public interface IResultFilter
        {
            void OnResultExecuting(ResultExecutingContext filterContext);
     
            void OnResultExecuted(ResultExecutedContext filterContext);
        }

    其中OnResultExecuting和OnResultExecuted方法分别是在Result执行前、后(页面展示内容生成前、后)触发。使用ResultFilter筛选器最典型的应用就是页面静态化。  

    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中,系统即会对异常进行对应的处理。

    5、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为主。 

    6、HandleErrorAttribte的使用

    MVC默认提供了一个异常过滤器 HandleErrorAttribte,我们有2种方式可以使用它:

    (1)HandleErrorAttribte的使用方法

    方式一:在类或者方法上直接使用HandleError

    // 在这里声明
    [HandleError]
    public class HomeController : Controller
    {
    // 或者在这里声明
    // [HandleError]
    public ActionResult Index()
    {
    return View();
    }
    }

    方式二:Global Filters功能来注册使用

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
    filters.Add(new HandleErrorAttribute());
    }

    public static void RegisterRoutes(RouteCollection routes)
    {
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
    "Default", // Route name
    "{controller}/{action}/{id}", // URL with parameters
    new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
    );

    }

    protected void Application_Start()
    {
    AreaRegistration.RegisterAllAreas();

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);
    }

    代码段里的filters.Add(new HandleErrorAttribute());设置是说整个程序所有的Controller都使用这个HandleErrorAttribute来处理错误。

    注意:HandleErrorAttribute只处理500系列错误,所以404错误需要另外单独处理,稍后会提到。

    (2)web.config配置

    接下来我们要做的是开启web.config根目录里的customErrors(不是views目录下的那个web.config哦),代码如下:

    <customerrors mode="On" defaultredirect="~/Error/HttpError">
    <error redirect="~/Error/NotFound" statuscode="404" />
    </customerrors>

    defaultredirect是设置为所有错误页面转向的错误页面地址,而里面的error元素可以单独定义不同的错误页面转向地址,上面的error行就是定义404所对应的页面地址。

    (3)定义错误页面

    就是定义我们所需要的错误页面的ErrorController:

    public class ErrorController : BaseController
    {
    //
    // GET: /Error/
    public ActionResult HttpError()
    {
    return View("Error");
    }
    public ActionResult NotFound()
    {
    return View();
    }
    public ActionResult Index()
    {
    return RedirectToAction("Index", "Home");
    }
    }

    默认Error的view是/views/shared/Error.cshtml文件,我们来改写一下这个view的代码,代码如下:

    @model System.Web.Mvc.HandleErrorInfo
    @{
    ViewBag.Title = "General Site Error";
    }

    <h2>A General Error Has Occurred</h2>

    @if (Model != null)
    {
    <p>@Model.Exception.GetType().Name<br />
    thrown in @Model.ControllerName @Model.ActionName</p>
    <p>Error Details:</p>
    <p>@Model.Exception.Message</p>
    }

     

  • 相关阅读:
    tomcat bug之部署应用的时候经常会发上startup failed due to previous errors
    maven编译项目理解
    MyReport报表引擎2.6.5.0新功能
    PHP入门-摘要表格处理问题
    EnumMap源代码阅读器
    MySQL几种方法的数据库备份
    工作日志2014-08-19
    Linux通过网卡驱动程序和版本号的信息
    JS于,子类调用父类的函数
    hdu 5007 水 弦
  • 原文地址:https://www.cnblogs.com/qtiger/p/9640865.html
Copyright © 2011-2022 走看看