【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方法访问。
原文地址:https://www.cnblogs.com/wangwust/p/6388710.html