zoukankan      html  css  js  c++  java
  • Filter in ASP.NET MVC

          在Asp.net MVC中,filter为cross-cutting concerns提供一个简单的实现方式。它共有4类Filter:

         

    下边分别来讲述。

          1. Authorization Filter

          Authorize filter可以用于action:

        [Authorize(Users = "adam, steve, bob", Roles = "admin")]
        public ActionResult Index()

    也可以直接用于controller:

        [Authorize(Roles = "Trader")]
        public class AdminController : Controller

    基于AuthorizeAttribute,我们扩展一个:

        public class CustomAuthorizeAttribute : AuthorizeAttribute
        {
            private string[] allowedUsers;

            public CustomAuthorizeAttribute() : this(new string[]{})
            {

            }

            public CustomAuthorizeAttribute(params string[] users)
            {
                this.allowedUsers = users;
            }

            protected override bool AuthorizeCore(HttpContextBase httpContext)
            {
                return httpContext.Request.IsAuthenticated && 
                    allowedUsers != null && 
                    allowedUsers.Contains(httpContext.User.Identity.Name, StringComparer.OrdinalIgnoreCase);

                //return httpContext.Request.IsLocal || base.AuthorizeCore(httpContext);
            }
        }

    使用方式:

            [CustomAuthorize("Tom""bob")]
            public ActionResult Manage()
            {
                return Content("");
            }

    这时候可以看到,Manage只有Tom和Bob可以访问,其他人都权限不足。如果没有登录,系统会跳转到登录界面,强制让你登录。

          显然,这种方式对于ajax请求不是很合理。好,让我们写一个ajax权限验证的filter:

        public class AjaxAuthorizeAttribute : AuthorizeAttribute
        {
            protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
            {
                if(filterContext.HttpContext.Request.IsAjaxRequest())
                {
                    filterContext.Result = new JsonResult
                                               {
                                                   Data = new {
                                                                  Error = "Not Authorized",
                                                                  LogOnUrl = new UrlHelper(filterContext.RequestContext).Action("LogOn""Account")
                                                              },
                                                   JsonRequestBehavior = JsonRequestBehavior.AllowGet
                                               };
                }
                else
                {
                    base.HandleUnauthorizedRequest(filterContext);
                }
            }
        }

    可以看到,我们是通过重写HandleUnauthorizedRequest方法,给访问权限不足的请求的response中不仅包含错误信息,还包含一个登录url,这样js中可以自由裁决如果处理了。

           2. Exception Filter

           exception filter只有当action被调用,有异常抛出时被触发。当然,异常可能来源于其他filer(authorization, action, 或者 result filter)、action自身、action结果被执行时。当异常被抛出时,如果没有exception filter将ExceptionContext的ExceptinHandled设置为true,mvc将调用默认的exception filter。

           来自定义一个:

        public class CustomExceptionAttribute : FilterAttribute, IExceptionFilter
        {
            public void OnException(ExceptionContext filterContext)
            {
                if(!filterContext.ExceptionHandled && filterContext.Exception is NullReferenceException)
                {
                    filterContext.Result = new RedirectResult("/SpecialErrorPage.html");
                    filterContext.ExceptionHandled = true;
                }
            }
        }

    可以看到,当有它捕获到异常后,跳往SpecialErrorPage.html。当异常发生时,就可以以一个人性化的页面来展示。使用如下:

            [CustomException]
            public ActionResult About()
            {
                //object nullObj = null;
                
    //var str = nullObj.ToString();

                return View();
            }

         另外,内置HandleExceptionAttribute具有ExceptionType、View、Master等属性,也可以灵活利用。如:

            [HandleError(ExceptionType = typeof(NullReferenceException), Master = null, View = "NullRefer")]
            public ActionResult IamError()
            {
                object nullObj = null;

                return Content(nullObj.ToString());
            }

    它可以捕获IamError内部的空引用异常,同时将以NullRefer页面来替代黄页。看页面代码:

    @Model HandleErrorInfo
    @{
        ViewBag.Title = "Sorry, there was a problem!";
    }
    <p>
    There was a <b>@Model.Exception.GetType().Name</b>
    while rendering <b>@Model.ControllerName</b>'s
    <b>@Model.ActionName</b> action.
    </p>
    <p>
    The exception message is: <b><@Model.Exception.Message></b>
    </p>
    <p>Stack trace:</p>
    <pre>@Model.Exception.StackTrace</pre>

    注意标黄部分,HandleErrorInfo是一个内置ViewModel,它宝航Exception、ControllerName、ActionName等属性。

          3. Action Filter

          先看IActionFilter接口的定义:

        public interface IActionFilter
        {
            void OnActionExecuting(ActionExecutingContext filterContext);
            void OnActionExecuted(ActionExecutedContext filterContext);
        }

    它们一个在action执行之前执行,一个在action执行之后执行。如下:

        public class ProfileAttribute : FilterAttribute, IActionFilter
        {
            private Stopwatch timer;

            public void OnActionExecuting(ActionExecutingContext filterContext)
            {
                //if(!filterContext.HttpContext.Request.IsSecureConnection)
                
    //{
                
    //    filterContext.Result = new HttpNotFoundResult();
                
    //}

                timer = Stopwatch.StartNew();
            }

            public void OnActionExecuted(ActionExecutedContext filterContext)
            {
                // do nothing

                timer.Stop();
                if (filterContext.Exception == null)
                {
                    filterContext.HttpContext.Response.Write(
                    string.Format("Action method elapsed time: {0}",
                    timer.Elapsed.TotalSeconds));
                }
            }
        }

    你可以通过自定义action filter,来对action执行时间进行监控。也可以如上文被注释的代码那样,直接在OnActionExecuting方法中给ActionResult赋值,而在OnActionExecuted中不做任何事情。

          4. Result Filter

          IResultFilter接口定义:

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

    和action filter一样,你也可以用上述2个方法对result执行时间监控计时。mvc有个内置filter,同时实现了action filter和result filter:

        [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
        public abstract class ActionFilterAttribute : FilterAttribute, IActionFilter, IResultFilter
        {
            #region IActionFilter Members

            public virtual void OnActionExecuting(ActionExecutingContext filterContext);
            public virtual void OnActionExecuted(ActionExecutedContext filterContext);

            #endregion

            #region IResultFilter Members

            public virtual void OnResultExecuting(ResultExecutingContext filterContext);
            public virtual void OnResultExecuted(ResultExecutedContext filterContext);

            #endregion
        }

    利用它,可以更方便的做监控了。如下:

        public class ProfileAllAttribute : ActionFilterAttribute
        {
            private Stopwatch timer;

            public override void OnActionExecuting(ActionExecutingContext filterContext)
            {
                timer = Stopwatch.StartNew();
            }

            public override void OnActionExecuted(ActionExecutedContext filterContext)
            {
                timer.Stop();
                filterContext.HttpContext.Response.Write(string.Format("Action method elapsed time: {0}", timer.Elapsed.TotalSeconds));
            }

            public override void OnResultExecuting(ResultExecutingContext filterContext)
            {
                timer = Stopwatch.StartNew();
            }

            public override void OnResultExecuted(ResultExecutedContext filterContext)
            {
                timer.Stop();
                filterContext.HttpContext.Response.Write(string.Format("Action result elapsed time: {0}", timer.Elapsed.TotalSeconds));
            }
        }

    它可以同时输出action方法和action result消耗的时间。

         5 Controller 和 filter

         实际上controller类同时实现了4类filter:

    public abstract class Controller : ControllerBase, IActionFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter

    所以,你可以在自己的controller里,重写上述多个方法,而不用再另外添加filter。

          6. 注册全局filter:

          如在RegisterGlobalFilters中加入:

                filters.Add(new ProfileAllAttribute());
                filters.Add(new HandleErrorAttribute()
                {
                    ExceptionType = typeof(NullReferenceException),
                    View = "SpecialError"
                });

    即可。

         7. filter排序执行:

         自定义一个filter,来试探它们的执行顺序:

        [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)]
        public class SimpleMessageAttribute : FilterAttribute, IActionFilter
        {
            public string Message { getset; }
            public void OnActionExecuting(ActionExecutingContext filterContext)
            {
                filterContext.HttpContext.Response.Write(
                string.Format("[Before Action: {0}]", Message));
            }

            public void OnActionExecuted(ActionExecutedContext filterContext)
            {
                filterContext.HttpContext.Response.Write(
                string.Format("[After Action: {0}]", Message));
            }
        } 

    测试如下:

        public class SimpleController : Controller
        {
            //[SimpleMessage(Message = "A")]
            
    //[SimpleMessage(Message = "B")]
            [SimpleMessage(Message = "A", Order = 2)]
            [SimpleMessage(Message = "B", Order = 1)]
            public ActionResult Index()
            {
                return View();
            }
        }

    启用备注时,注释后两行时,输出为:

    before A -> Before B -> after B -> after A,但是MVC不保证每次都是这样的(A和B的执行顺序不能保证)。为了确保顺序额,如上文代码,指定Order顺序,这个时候铁定是:Before B -> Before A -> after A -> after B。 

          如果你在多个地方指定不同filter的order,它会优先global中的,然后是controller的,最后才是action的。

          mvc还有其他一些内置的filter,如OutputCache等,这里不再描述。

    源码download 

  • 相关阅读:
    读財报:交易额增长111%的京东,为毛还是亏?
    HDU 1548 A strange lift 搜索
    linux两台server远程copy文件
    Nginx 反向代理、负载均衡、页面缓存、URL重写及读写分离具体解释
    js事件防止冒泡
    LeetCode Rotate Array
    ARM架构授权和IP核授权有什么不一样啊?
    Linux系统结构 详解
    mount -o
    ARM开发板系统移植-----u-boot的编译
  • 原文地址:https://www.cnblogs.com/Langzi127/p/2747482.html
Copyright © 2011-2022 走看看