zoukankan      html  css  js  c++  java
  • 【ASP.NET MVC 学习笔记】- 12 Filter

    本文参考:http://www.cnblogs.com/willick/p/3331520.html

    1、Filter(过滤器)是基于AOP(Aspect-Oriented Programming 面向切面编程)的设计。作用是对MVC框架处理客户端请求注入额外的逻辑,以非常优美简单的方式实现横切关注点(Cross-cutting Concerns)。所谓横切关注点是指横越应用程序多个甚至所有模块的功能,经典的横切关注点有日志记录、缓存处理、异常处理和权限验证等。

    2、MVC框架支持的Filter可以分为四类,每一个类都可以在处理请求的不同时间点注入额外的逻辑处理。这四类Filter如下图:

      

       其中ActionFilter是一个抽象类,使用之前必须对它进行实现;而另外两个则有默认实现可以直接调用。这些Filter既可以用在单个Action上,也可以用在整个Controller上。

       对于自定义的Controller基类,应用于该基类的Filter也将对继承自该基类的子类有效。

    2、Authorization Filter是在Action和其他种类的Filter之前运行的,作用是强制实施权限策略,保证Action只被授权了的用户调用。它实现的接口如下:

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

       我们可以通过继承IAuthorizationFilter接口自定义Authorization Filter。下列示例自定义了一个Filter用于验证是否允许本地请求。

    //AuthorizeAttribte 类帮我们内置地实现了很多东西,我们只需把重点放在 AuthorizeCore 方法上,在该方法中实现权限认证的逻辑。
    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; } } }
    [Authorize(Users = "jim, steve, jack", Roles = "admin")]//使用内置的Authorization Filter,该语句的意思是只允许角色为admin、且用户名必须是jim、steve、jack中的一个的用户访问该Action
    public string Index() 
    { return "This is the Index action on the Home controller"; }

    3、Exception Filter在下面三种来源抛出未处理异常时运行:

    • 另外一种Filter(如Authorization、Action或Result等Filter)。
    • Action方法本身。
    • Action方法执行完成(即处理ActionResult的时候)。

        Exception Filter 必须实现接口IExceptionFilter,该接口定义为:

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

        ExceptionContext继承自ControllerContext,后者的常用属性说明:

    • Controller,返回当前请求的controller对象。
    • HttpContext,提供请求和响应的详细信息。
    • IsChildAction,如果是子action则返回true(稍后将简单介绍子action)。
    • RequestContext,提供请求上下文信息。
    • RouteData,当前请求的路由实例信息。

        ExceptionContext的常用属性说明:

    • ActionDescriptor,提供action方法的详细信息。
    • Result,是一个 ActionResult 类型,通过把这个属性值设为非空可以让某个Filter的执行取消。
    • Exception,未处理异常信息。
    • ExceptionHandled,如果另外一个Filter把这个异常标记为已处理则返回true。

        一个Exception Filter可以通过把 ExceptionHandled 属性设置为true来标注该异常已被处理过,这个属性一般在某个action方法上应用了多个Exception Filter时会用到。ExceptionHandled 属性设置为true后,就可以通过该属性的值来判断其它应用在同一个action方法上的Exception Filter是否已经处理了这个异常,以免同一个异常在不同的Filter中重复被处理。示例:

    //1、Filter的定义,通过重定向到Content目录下的一个静态html文件来显示友好的 ArgumentOutOfRangeException 异常信息。
    //RangeExceptionAttribute 类继承了FilterAttribute类,并且实现了IException接口。
    //作为一个MVC Filter,它的类必须实现IMvcFilter接口,你可以直接实现这个接口,但更简单的方法是继承 FilterAttribute 基类
    public class RangeExceptionAttribute : FilterAttribute, IExceptionFilter 
    {
        public void OnException(ExceptionContext filterContext) 
        {
            if (!filterContext.ExceptionHandled && filterContext.Exception is ArgumentOutOfRangeException)
             {
                filterContext.Result = new RedirectResult("~/Content/RangeErrorPage.html");
                filterContext.ExceptionHandled = true;
            }
        }
    }
    
    //2、RangeErrorPage.html页面
    <!DOCTYPE html> 
    <html xmlns="http://www.w3.org/1999/xhtml"> 
    <head> 
        <title>Range Error</title> 
    </head> 
    <body> 
        <h2>Sorry</h2> 
        <span>One of the arguments was out of the expected range.</span> 
    </body> 
    </html>
    
    //3、HomeController中添加一个值越限时抛出异常的action
     public class HomeController : Controller 
    { 
            [RangeException]
            public string RangeTest(int id)
            { 
                if (id > 100)
                { 
                    return String.Format("The id value is: {0}", id); 
                }
                else
                { 
                    throw new ArgumentOutOfRangeException("id", id, ""); 
                } 
            } 
     } 

        由于静态的html文件是和后台脱离的,所以实际项目中更多的是用一个View来呈现友好的错误信息,以便很好的对它进行一些动态的控制:

    //1、定义Filter
    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 ViewResult
                    {
                        ViewName = "RangeError",
                        ViewData = new ViewDataDictionary<int>(val)
                    };
                    filterContext.ExceptionHandled = true;
                }
            }
     }        
    
    //2、RangeError.cshtml
    @model int
    
    <!DOCTYPE html> 
    <html> 
    <head> 
        <meta name="viewport" content="width=device-width" /> 
        <title>Range Error</title> 
    </head> 
    <body> 
        <h2>Sorry</h2> 
        <span>The value @Model was out of the expected range.</span> 
        <div> 
            @Html.ActionLink("Change value and try again", "Index") 
        </div> 
    </body> 
    </html> 

    4、程序发布后不应该显示异常信息给用户看。我们可以通过配置Web.config让应用程序不管在何时何地引发了异常(即使是在View中的异常)都可以显示统一的友好错误信息。在Web.config文件中的<system.web>节点下添加如下子节点:

    <system.web><customErrors mode="On" defaultRedirect="/Content/RangeErrorPage.html"/>//这个配置只对远程访问有效,本地运行站点依然会显示跟踪信息。
    </system.web>

    5、MVC框架内置的 HandleErrorAttribute包含ExceptionType、View和Master三个属性。当ExceptionType属性指定类型的异常被引发时,这个Filter将用View属性指定的View(使用默认的Layout或Mast属性指定的Layout)来呈现一个页面。示例:

    [HandleError(ExceptionType = typeof(ArgumentOutOfRangeException), View = "RangeError")] 
    public string RangeTest(int id) 
    { 
        if (id > 100) 
        { 
            return String.Format("The id value is: {0}", id); 
        } else 
        { 
            throw new ArgumentOutOfRangeException("id", id, ""); 
        } 
    } 

        使用内置的HandleErrorAttribute,将异常信息呈现到View时,这个特性同时会传递一个HandleErrorInfo对象作为View的model。HandleErrorInfo类包含ActionName、ControllerName和Exception属性,如下面的 RangeError.cshtml 使用这个model来呈现信息:

    @model HandleErrorInfo 
    @{ 
        ViewBag.Title = "Sorry, there was a problem!"; 
    } 
     
    <!DOCTYPE html> 
    <html> 
    <head> 
        <meta name="viewport" content="width=device-width" /> 
        <title>Range Error</title> 
    </head> 
    <body> 
        <h2>Sorry</h2> 
        <span>The value @(((ArgumentOutOfRangeException)Model.Exception).ActualValue) 
            was out of the expected range.</span>         
        <div> 
            @Html.ActionLink("Change value and try again", "Index") 
        </div> 
        <div style="display: none"> 
            @Model.Exception.StackTrace 
        </div> 
    </body> 
    </html>

    6、ActionFilter是对Action方法的执行进行筛选的,包括执行前和执行后。它实现了以下接口:

    namespace System.Web.Mvc 
    { 
        public interface IActionFilter 
        { 
    //在action方法执行之前被调用
    void OnActionExecuting(ActionExecutingContext filterContext);

    //在action方法执行之后被调用
    void OnActionExecuted(ActionExecutedContext filterContext); } }

        下列示例自定义了一个ActionFilter:

    //自定义ActionFilter
    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>Action method elapsed time: {0}</div>", timer.Elapsed.TotalSeconds)); } } } //在HomeController中添加一个Action并应用该Filter [ProfileAction] public string FilterTest()
    {
    return "This is the ActionFilterTest action"; }

    7、Result Filter用来处理action方法返回的结果,是在Action Filter之后执行的。用法和Action Filter类似,它需要实现 IResultFilter 接口,定义如下:

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

        示例代码:

    //1、Filter 定义
    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)); 
        } 
    }
    
    //2、应用
    [ProfileAction] 
    [ProfileResult] 
    public string FilterTest() 
    { 
        return "This is the ActionFilterTest action"; 
    } 

    8、MVC框架内置了一个 ActionFilterAttribute 类用来创建action 和 result 筛选器,即可以控制action方法的执行也可以控制处理action方法返回结果。它是一个抽象类,定义如下:

    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) 
            { 
            } 
        } 
    }

         使用这个抽象类方便之处是你只需要实现需要加以处理的方法。示例:

    //1、定义
    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)); 
        } 
    }
    
    //2、应用
    [ProfileAction] 
    [ProfileResult] 
    [ProfileAll] 
    public string FilterTest() 
    { 
        return "This is the FilterTest action"; 
    } 

        我们也可以Controller中直接重写 ActionFilterAttribute 抽象类中定义的四个方法,效果和使用Filter是一样的,例如:

    public class HomeController : Controller 
    { 
        private Stopwatch timer; 
        ...
        public string FilterTest() 
        { 
            return "This is the FilterTest action"; 
        } 
    
        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)); 
        } 
    } 

    9、全局Filter对整个应用程序的所有controller下的所有action方法有效。在App_Start/FilterConfig.cs文件中的RegisterGlobalFilters方法,可以把一个Filter类注册为全局,如:

    public class FilterConfig 
    { 
        public static void RegisterGlobalFilters(GlobalFilterCollection filters) 
        { 
           filters.Add(new HandleErrorAttribute()); 
           filters.Add(new ProfileAllAttribute()); //如此,ProfileAllAttribute将对所有的action有效
        } 
    }

    10、MVC框架内置了很多Filter,常见的有RequireHttps、OutputCache、AsyncTimeout等等。下面是几个常用的:

    • RequireHttps,强制使用HTTPS协议访问。它将浏览器的请求重定向到相同的controller和action,并加上 https:// 前缀。
    • OutputCache,将action方法的输出内容进行缓存。
    • AsyncTimeout/NoAsyncTimeout,用于异步Controller的超时设置。
    • ChildActionOnlyAttribute,使用action方法仅能被Html.Action和Html.RenderAction方法访问。
  • 相关阅读:
    LeetCode121.买卖股票的最佳时机
    OpenFunction 应用系列之一: 以 Serverless 的方式实现 Kubernetes 日志告警
    KubeSphere 核心架构浅析
    云原生爱好者周刊:服务网格的困境与破局
    DG:11.2.0.4 RAC在线duplicate恢复DG
    ORA-17629: Cannot connect to the remote database server
    DG:RFS[8]: No standby redo logfiles created for thread 2
    U盘内容不显示?U盘有文件却看不见?
    【CSS】特殊符号content编码及作为字体图标使用方法
    Python中的if __name__ == '__main__'(转载)
  • 原文地址:https://www.cnblogs.com/wangwust/p/6388710.html
Copyright © 2011-2022 走看看