zoukankan      html  css  js  c++  java
  • ASP.NET MVC 4 (三) 过滤器

    先来看看一个例子演示过滤器有什么用:

    复制代码
    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 
    } 
    ...
    复制代码

    AdminController控制器的众多Action中我们都需要判定当前验证用户,这里有很多重复的代码,我们可以简化为:

    复制代码
    [Authorize] 
    public class AdminController : Controller { 
    // ... instance variables and constructor 
    public ViewResult Index() { 
    // ...rest of action method 
    } 
    public ViewResult Create() { 
    // ...rest of action method 
    } 
    ...
    复制代码

    Authorize特性类AuthorizeAttribute就称作MVC的Filter,它在横向为MVC框架扩展功能,让我们可以更方便的处理日志、授权、缓存等而不影响纵向主体功能。

    MVC常用的滤器类型:

    • Authorization:实现IAuthorizationFilter接口,默认实现类AuthorizeAttribute,在调用Action方法前首先处理认证信息。
    • Action:实现IActionFilter接口,默认实现类ActionFilterAttribute,在运行Action前后调用实现一些额外的动作。
    • Result:实现IResultFilter接口,默认实现类ActionFilterAttribute,在action result运行前后调用实现额外的动作。
    • Exception:实现IExceptionFilter接口,默认实现类HandleErrorAttribute,仅在其他过滤器或者action方法或者action result抛出异常时调用。

    过滤器可以应用在整个控制器类上,也可以单对某个Action方法,当然也可以是同时应用多个过滤器:

    复制代码
    [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 
    } 
    } 
    复制代码

    Authorization过滤器

    Authorization过滤器用于控制仅授权的用户可以调用某个Action方法,它必须实现IAuthorizationFilter接口:

    namespace System.Web.Mvc { 
    public interface IAuthorizationFilter { 
    void OnAuthorization(AuthorizationContext filterContext); 
    } 
    } 

    处理安全方面的代码必须谨慎全面,一般我们不要直接实现接口IAuthorizationFilter,而从AuthorizeAttribute扩展自定义类:

    复制代码
    public class CustomAuthAttribute : AuthorizeAttribute { 
      private bool localAllowed; 
      public CustomAuthAttribute(bool allowedParam) { 
        localAllowed = allowedParam; 
      } 
      protected override bool AuthorizeCore(HttpContextBase httpContext) { 
        if (httpContext.Request.IsLocal) { 
        return localAllowed; 
        } else { 
        return true; 
        } 
      } 
    } 
    复制代码

    这里定义了CustomAuthAttribute过滤器,如果请求来自于非本机总是允许,如果是本机请求则视传入参数allowedParam而定:

    public class HomeController : Controller { 
      [CustomAuth(false)] 
      public string Index() { 
        return "This is the Index action on the Home controller"; 
      } 
    } 

     默认实现AuthorizeAttribute足够应付多数授权功能限制,我们可以传入参数限制访问的用户或者角色:

    [Authorize(Users = "adam, steve, jacqui", Roles = "admin")] 

    需要注意的是AuthorizeAttribute仅仅负责授权,而用户的认证则依赖于ASP.NET的认证机制。

    Exception过滤器

    Exception过滤器需要实现IExceptionFilter接口:

    namespace System.Web.Mvc { 
    public interface IExceptionFilter { 
    void OnException(ExceptionContext filterContext); 
    } 
    } 

    从filterContext我们可以获取很多相关信息,比如Controller、HttpContext、IsChildAction、 RouteData、Result、Exception、ExceptionHandled等。异常的具体信息我们可以从Exception获取,我们设 置ExceptionHandled为true报告异常已经处理,在设置为true之前最好检查异常是否已经被action方法上其他的过滤器处理,以避 免重复的错误纠正动作。如果异常未被处理(ExceptionHandled!=true),MVC框架使用默认的异常处理程序显示ASP.NET的黄屏 错误页面。

    我们可以创建自定义的异常过滤器:

    复制代码
     public class RangeExceptionAttribute : FilterAttribute, IExceptionFilter {
    
            public void OnException(ExceptionContext filterContext) {
    
                if (!filterContext.ExceptionHandled &&
                    filterContext.Exception is ArgumentOutOfRangeException) {
    
                         int val = (int)(((ArgumentOutOfRangeException)filterContext.Exception).ActualValue);
    
                        //filterContext.Result = new RedirectResult("~/Content/RangeErrorPage.html");
                        filterContext.Result = new ViewResult { 
                            ViewName = "RangeError", 
                            ViewData = new ViewDataDictionary<int>(val)
                        };
                        filterContext.ExceptionHandled = true;
                }
            }
        }
    复制代码

    注意这里RangeExceptionAttribute除了实现IExceptionFilter接口还从FilterAttribute继承, 这是因为MVC的过滤器类还必须实现IMvcFilter接口,这里我们没有直接实现IMvcFilter接口,改为从默认封装类 FilterAttribute继承。如下使用这个自定义过滤器:

    复制代码
    ... 
    [RangeException] 
    public string RangeTest(int id) { 
      if (id > 100) { 
      return String.Format("The id value is: {0}", id); 
      } else { 
      throw new ArgumentOutOfRangeException("id"); 
      } 
    } 
    ... 
    复制代码

    在发生异常时过滤器会将我们重定向到RangeError视图。

    通常我们不需要创建自己的异常处理器,而使用MVC提供的默认过滤器HandleErrorAttribute:

    [HandleError(ExceptionType = typeof(ArgumentOutOfRangeException), View = "RangeError",Master="")]

    属性ExceptionType指定要处理的异常类型,如果不指定则处理所有异常类型;view指定错误时要渲染的视;master指定所用的页面布局。

    在使用HandleErrorAttribute过滤前我们必须在Web.config的<System.Web>一节启用CusomErrors:

    <customErrors mode="On" defaultRedirect="/Content/RangeErrorPage.html"/> 

    默认customErros为RemoteOnly,defaultRedirect指定一个默认的错误页面。

    Action过滤器

    Action过滤器必须实现IActionFilter接口:

    namespace System.Web.Mvc { 
    public interface IActionFilter { 
      void OnActionExecuting(ActionExecutingContext filterContext); 
      void OnActionExecuted(ActionExecutedContext filterContext); 
      } 
    } 

    OnActionExecuting()在调用action方法前调用,OnActionExecuted()则在调用action方法后调用。

    OnActionExecuting的一个实现例子:

    复制代码
    public class CustomActionAttribute : FilterAttribute, IActionFilter {
    
            public void OnActionExecuting(ActionExecutingContext filterContext) {
                if (filterContext.HttpContext.Request.IsLocal) {
                    filterContext.Result = new HttpNotFoundResult();
                }
            }
    
            public void OnActionExecuted(ActionExecutedContext filterContext) {
                // not yet implemented
            }
        }
    复制代码

    这里我们仅实现了OnActionExecuting方法,在从本地请求任何应用此过滤器的action方法时返回404错误。

    OnActionExecuted的一个例子:

    复制代码
     public class ProfileActionAttribute : 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("<div>profile result method elapsed time: {0}</div>",
                            timer.Elapsed.TotalSeconds));
                }
            }
        }
    复制代码

    在调用action方法前我们启动计时器,在action方法调用后停止计时器并输出计时器所计action方法运行时长。

    Result过滤器

    Result过滤器实现IResultFilter接口:

    namespace System.Web.Mvc { 
    public interface IResultFilter { 
    void OnResultExecuting(ResultExecutingContext filterContext); 
    void OnResultExecuted(ResultExecutedContext filterContext); 
    } 
    } 

    Result过滤器操作的是action方法返回结果,有意思的是即使action方法返回void所应用的Result过滤器也会动作。

    类似上面的ProfileAction过滤器我们可以对Result运行计时:

    复制代码
    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("<div>Result elapsed time: {0}</div>",
                            timer.Elapsed.TotalSeconds));
            }
        }
    复制代码

    内建的Result过滤器类ActionFilterAttribute

    MVC为我们提供了ActionFilterAttribute类,它同时实现了action和result过滤器接口:

    复制代码
    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) { 
    } 
    } 
    } 
    复制代码

    我们只需要继承该类并重载需要的方法,比如:

    复制代码
    public class ProfileAllAttribute : ActionFilterAttribute {
            private Stopwatch timer;
    
            public override void OnActionExecuting(ActionExecutingContext filterContext) {
                timer = Stopwatch.StartNew();
            }
    
            public override void OnResultExecuted(ResultExecutedContext filterContext) {
                timer.Stop();
                filterContext.HttpContext.Response.Write(
                        string.Format("<div>Total elapsed time: {0}</div>",
                            timer.Elapsed.TotalSeconds));
            }
        }
    复制代码

     Controller类的过滤器支持

    MVC的Controller类内部实现了IAuthorizationFilter、IActionFilter、IResultFilter、IExceptionFilte四个接口,并提供OnXXX的虚函数供调用,比如:

    复制代码
        public class HomeController : Controller {
            private Stopwatch timer;
    
            protected override void OnActionExecuting(ActionExecutingContext filterContext)
            {
                timer = Stopwatch.StartNew();
            }
    
            protected override void OnResultExecuted(ResultExecutedContext filterContext)
            {
                timer.Stop();
                filterContext.HttpContext.Response.Write(
                        string.Format("<div>Total elapsed time: {0}</div>",
                            timer.Elapsed.TotalSeconds));
            } 
    
    ...
    复制代码

    全局过滤器

    全局过滤器应用于应用程序内所有控制器的所有action方法,我们在App_Start/FilterConfig.cs可以注册全局过滤器:

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

    HandleErrorAttribute是VS为我们默认添加的,用于未处理异常错误时显示/Views/Shared /Error.cshtml页面;ProfileAllAttribute则是我们添加的自定义过滤器,运行任何action方法时都会调用这个过滤器。

    其他MVC内建过滤器

    MVC框架还内建提供以下过滤器:

    • RequireHttps:指示必须以HTTPS访问action方法,仅用于HTTP get方法
    • OutputCache:缓存action方法的结果
    • ValidateInput和ValidationAntiForgeryToken:安全授权相关的过滤器
    • AsnycTimeOut和NoAsyncTimeout:用于异步控制器
    • ChildActionOnlyAttribute:用于授权Html.Action或者Html.RenderAction调用子action方法

    这些过滤器的使用方法可以参见MSDN。

    过滤器的运行顺序

    过滤器的运行按先后顺序是:authorization过滤器、action过滤器、result过滤器,期间任何时刻发生未处理异常调用异常处理 器。这是针对不同类型的过滤器,但是如果所应用的是同一类的过滤器呢?MVC默认并不保证同类型过滤器的调用程序,也就是说很可能并非按照出现在代码中的 先后程序来调用过滤器,但是我们可以显式的指定它们的调用顺序:

    复制代码
    ... 
    [SimpleMessage(Message="A", Order=2)] 
    [SimpleMessage(Message="B", Order=1)] 
    public ActionResult Index() { 
    Response.Write("Action method is running"); 
    return View(); 
    } 
    ... 
    复制代码

    这里SimpleMessage是一个action过滤器,通过order我们指定它们的运行顺序: B->OnActionExecuting、A->OnActionExecuting、A->OnActionExecuted、 B->OnActionExecuted。注意A的OnActionExecuted先于B的OnActionExecuted,和 OnActionExecuting正好相反,这是无法改变的。在不指定order时,MVC内部指定为-1。另外如果同类型过滤器指定相同的order 比如都是1,还要根据在哪里应用的过滤器来分先后执行:首先执行的是全局过滤器、然后是控制器类上、最后才是action方法;例外的是 exception过滤器,它的顺序正好与此相反

  • 相关阅读:
    面向对象、面向接口、面向方法编程的区别?
    面向接口、对象、方面编程区别 -- 精简版
    面向接口编程详解(一)——思想基础
    吴裕雄--天生自然数据结构:静态链表及其创建
    吴裕雄--天生自然数据结构:单链表的基本操作
    吴裕雄--天生自然数据结构:单链表,链式存储结构
    吴裕雄--天生自然数据结构:顺序表的基本操作
    吴裕雄--天生自然Python Matplotlib库学习笔记:matplotlib绘图(2)
    吴裕雄--天生自然Python Matplotlib库学习笔记:matplotlib绘图(1)
    吴裕雄--天生自然Numpy库学习笔记:NumPy Matplotlib
  • 原文地址:https://www.cnblogs.com/zxtceq/p/6076959.html
Copyright © 2011-2022 走看看