本文参考: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方法访问。