ASP.NETMVC5基础-过滤器(Filters)详解
什么是过滤器?
通过上⼀篇关于Controller控制器的⽂章我们知道,MVC中的每⼀个请求,都会分配给相应的控制器(Controller)和对应的⾏为⽅法(Action)去处理,那么如果我们想要在Action处理的前后加上⼀些额外的处理逻辑怎么办呢?这时候就⽤到了过滤器(Filters)。
在ASP.NET MVC的请求处理过程中有19个管道事件,这些事件分布在请求处理的各个节点中,⽐如BeginRequest(开始处理请求时触发)、AuthenticateRequest(对请求进⾏⾝份验证时触发)、AuthorizeRequest(对请求进程授权时触发)…等等等等。⽽过滤器的主要作⽤就是将我们的附加逻辑注⼊到这些请求处理管道中。
在实际业务中,在Action⽅法前后添加额外附加逻辑的情况有很多,过滤器就是⽤来完成此功能。通过过滤器可以将与业务逻辑⽆关但经常需要执⾏的代码分离开,使我们的代码逻辑性更加清晰,代码更加简洁。
过滤器的类型与作⽤
MVC给我们提供了四种过滤器,基本满⾜了我们实际业务中常⽤的需求,包括以下:
过滤器类型
名称
实现的接⼝默认的实现类作⽤执⾏的顺序与节点
授权过滤器IAuthorizationFilter AuthorizeAttribute ⽤于限制进⼊控制器或控制器的某个⾏为
⽅法
在控制器⽅法调⽤前执⾏,所有过滤器中
最先执⾏的
动作过滤器IActionFilter ActionFilterAttribute⽤于进⼊动作⽅法之前或之后的处理在控制器⽅法调⽤前/后执⾏
结果过滤器IResultFilter ActionFilterAttribute⽤于动作⽅法返回结果之前或之后的处理在控制器⽅法调⽤完,跳转⾄view页⾯前/后执⾏
异常处理过滤器IExceptionFilter HandleErrorAttribute⽤于处理某个动作⽅法或某个控制器⾥⾯
抛出的异常
在控制器⽅法抛出异常时执⾏
这四种类型的接⼝是MVC对过滤器的⼀个接⼝规范,同时MVC默认通过AuthorizeAttribute(授权)、HandleErrorAttribute(异常处理)、ActionFilterAttribute(动作和结果)三个类实现了这四个接⼝。
需要注意的是ActionFilterAttribute类既实现了IActionFilter接⼝,也实现了IResultFilter接⼝。这是个抽象类,要求必须提供⼀个实
现,AuthorizeAttribute和HandleErrorAttribute类则包含了⼀些有⽤的特性,可以不必创建派⽣类进⾏使⽤。所以我们⼀般都会通过继承ActionFilterAttribute类,实现⾃定义的过滤器。
除以上接⼝之外,我们还要⽤到FilterAttribute类,这个类将我们的过滤器包装成了特性,使我们的过滤器可以⽅便的在Action⽅法上⽅使⽤。
定义过滤器
过滤器有以下⼏个特点:
可⽤于动作⽅法(Action)
可⽤于控制器(Controller)
可多个Filter同时使⽤
不同级别可以混搭
可运⽤于基类的过滤器,会影响该基类的所有派⽣类
下⾯我们逐⼀介绍下基本过滤器的使⽤⽅法。
授权过滤器
所有实现了IAuthorizationFilter接⼝的都可以称之为授权过滤器。它的接⼝定义如下:
namespace System.Web.Mvc
{
//
// 摘要:
/
/ 定义授权筛选器所需的⽅法。
public interface IAuthorizationFilter
{
//
// 摘要:
// 在需要授权时调⽤。
//
// 参数:
// filterContext:
// 筛选器上下⽂。
void OnAuthorization(AuthorizationContext filterContext);
}
}
授权过滤器是最先运⾏的过滤器,它运⾏在其它过滤器和Action⽅法之前。客户端请求在调⽤Action之前,MVC框架会检测Action上是否有授权过滤器,如果有会调⽤OnAuthorization⽅法,如果此⽅法批准了请求,才会调⽤相应的Action。流程如图:
MVC默认使⽤AuthorizeAttribute实现了IAuthorizationFilter接⼝,所以我们可以在Action⽅法上直接添加Authorize特性标签来验证授权:
打开Index页⾯,会显⽰⽆权限:
由于使⽤的是MVC⾃带的授权验证⽅法,未能符合它的验证机制,所以⽆权限查看。通常我们需要添加⼀个新的派⽣⾃AuthorizeAttribute类的授权过滤器来完成我们⾃⼰业务逻辑。
下⾯我们⾃定义⼀个授权过滤器。我们在MVC项⽬中添加⼀个Filters⽂件夹,我们所有⾃定义的过滤器都可以放到这个⽂件夹下,便于管理。
在Filters下创建⼀个类,类名为MyAuthorizeAttribute。需要注意,过滤器要以Attribute结尾,这是MVC的约定。代码如下:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
//重写授权检查⽅法,返回值为true,允许访问,false,禁⽌访问。
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
//请求参数user为空,禁⽌访问
if (string.IsNullOrEmpty(HttpContext.Current.Request.QueryString["user"]))
{
return false;
}
return true;
}
}
可以看到,我们只要重写AuthorizeCore⽅法就可以根据我们的业务需求判断是否有权限访问,返回值为true允许访问,返回值为false禁⽌访问。
回到HomeController,我们给About⽅法加上我们⾃定义的特性:
我们看看效果:
可以看到,当About页⾯没有user参数时,会提⽰⽆权限,有user参数则可以访问通过。
在实际业务中我们可以使⽤授权过滤器来管理⽤户登录状态的授权验证。当然,我的这个例⼦只是基础的⽤法,实际业务⽐这复杂的多,那么就需要我们⾃⼰去思考设计授权过滤器⽅法了。
动作过滤器
动作过滤器需要实现IActionFilter接⼝,接⼝定义如下:
//
// 摘要:
// 定义操作筛选器中使⽤的⽅法。
public interface IActionFilter
{
//
// 摘要:
// 在执⾏操作⽅法后调⽤。
//
// 参数:
// filterContext:
// 筛选器上下⽂。
void OnActionExecuted(ActionExecutedContext filterContext);
//
// 摘要:
// 在执⾏操作⽅法之前调⽤。
//
// 参数:
// filterContext:
// 筛选器上下⽂。
void OnActionExecuting(ActionExecutingContext filterContext);
}
我们看到该接⼝⾥有两个⽅法OnActionExecuting和OnActionExecuted,前者在动作⽅法执⾏前调⽤,后者在动作⽅法执⾏后调⽤。
OnActionExecuting⽅法是在Action⽅法执⾏前调⽤的,那么我们可以利⽤这个⽅法来检测请求,并且可以在这⾥修改请求,取消请求等等操作。
OnActionExecuting⽅法的参数是⼀个ActionExecutingContext对象,它继承⾃ControllerContext类,属性如下:
名称类型说明
ActionDescriptor ActionDescriptor获取或设置操作描述符。
ActionParameters IDictionary<string, object>获取或设置操作⽅法参数。
Result ActionResult获取或设置由操作⽅法返回的结果。
我们添加⼀个⾃定义的Action过滤器。由于ActionFilterAttribute类实现了IActionFilter接⼝,所以我们直接继承ActionFilterAttribute类即可,并且重写OnActionExecuting和OnActionExecuted⽅法。如下:
public class MyActionAttribute : ActionFilterAttribute
{
/
//<summary>
/// Action调⽤之前运⾏
///</summary>
///<param name="filterContext"></param>
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (string.Equals(filterContext.HttpContext.Request.HttpMethod, "get", StringComparison.CurrentCultureIgnoreCase))
{
filterContext.Result = new HttpNotFoundResult("只允许POST请求!");
}
}
///<summary>
/// Action调⽤之后运⾏
///</summary>
///<param name="filterContext"></param>
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
}
}
我们给Index⽅法添加上MyAction特性标签:
打开Index页,显⽰如下:
可以看到,页⾯返回了404错误,提⽰信息为我们设置的Message。
spring framework网络系统参数OnActionExecuted⽅法在Action操作⽅法调⽤之后执⾏,传递给OnActionExecuted⽅法的参数是ActionExecutedContext对象。这个类⽐ActionExecutingContext对象多了些属性,如下:
名称类型说明
ActionDescriptor ActionDescriptor获取或设置操作描述符。
Canceled bool获取或设置⼀个值,该值指⽰此ActionExecutedContext 对象已被取消。
Exception Exception获取或设置在操作⽅法的执⾏过程中发⽣的异常(如果有)。
ExceptionHandled bool获取或设置⼀个值,该值指⽰是否处理异常。
Result ActionResult获取或设置由操作⽅法返回的结果。
我们可以通过OnActionExecuted⽅法来执⾏⼀些跨越动作⽅法的任务,⽐如我们可以⽤它来获取动作⽅法执⾏的时间。我们修改MyActionAttribute 过滤器代码如下:
public class MyActionAttribute : ActionFilterAttribute
{
private Stopwatch timer;
///<summary>
/// Action调⽤之前运⾏
///</summary>
///<param name="filterContext"></param>
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
timer = Stopwatch.StartNew();
}
///<summary>
/// Action调⽤之后运⾏
/
//</summary>
///<param name="filterContext"></param>
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
timer.Stop();
filterContext.HttpContext.Response.Write($"<div>⽅法执⾏时间:{timer.Elapsed.TotalSeconds:F6}s</div>");
}
}
我们在⽅法启动之前启动了⼀个计时器,在⽅法执⾏后停⽌了它,并且将这个时间间隔输出到我们的页⾯上。重新编译打开Index页⾯,显⽰如下:
结果过滤器
结果过滤器,顾名思义针对的是动作⽅法返回的结果,它在我们的动作⽅法结果返回前后执⾏。
创建结果过滤器需要实现IResultFilter接⼝。ActionFilterAttribute类帮我们实现了IResultFilter接⼝,我们可以直接继承ActionFilterAttribute创建我们的过滤器,然后通过重写OnResultExecutin和OnResultExecuting(在执⾏操作结果后调⽤)⽅法来实现过滤器规则。
OnResultExecuting⽅法会在执⾏操作结果前调⽤,这个⽅法的参数是⼀个ResultExecutingContext对象,属性如下:
名称类型说明
Cancel bool获取或设置⼀个值,该值指⽰此 ResultExecutingContext 值是否为“cancel”。
Result ActionResult获取或设置操作结果。
OnResultExecuted⽅法在执⾏操作结果后调⽤,这个⽅法的参数是⼀个ResultExecutingContext对象,属性如下:
名称类型说明
Canceled bool获取或设置⼀个值,该值指⽰此 ResultExecutingContext 值是否为“cancel”。
Exception Exception获取或设置在操作⽅法的执⾏过程中发⽣的异常(如果有)。
ExceptionHandled bool获取或设置⼀个值,该值指⽰是否处理异常。
Result ActionResult获取或设置操作结果。
我们可以使⽤这两个⽅法在Action⽅法返回结果前后进⾏操作,具体操作的代码我就不赘述了。
异常处理过滤器
异常处理过滤器需要实现的接⼝为IExceptionFilter。我们看下接⼝的定义:
//
// 摘要:
// 定义异常筛选器所需的⽅法。
public interface IExceptionFilter
{
/
/
// 摘要:
// 在发⽣异常时调⽤。
//
// 参数:
// filterContext:
// 筛选器上下⽂。
void OnException(ExceptionContext filterContext);
}
接⼝⽅法OnException可以看到它在我们的⽅法中出现异常时触发,MVC默认⽤HandleErrorAttribute类来实现了此接⼝,我们⾃⼰定义的异常过滤器可以继承此类进⾏扩展。
OnException⽅法中传递的参数是⼀个ExceptionContext对象,它的属性如下:
名称类型说明
Exception Exception获取或设置异常对象。
ExceptionHandled bool获取或设置⼀个值,该值指⽰是否已处理异常。
Result ActionResult获取或设置操作结果。
我们来定义⼀个异常过滤器,代码如下:
public class MyExceptionAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
//如果有异常,跳转到异常页⾯。
if (filterContext.Exception != null)
{
//跳转到⾃定义的错误页
ActionResult view = new ViewResult() { ViewName = "Error" };
filterContext.Result = view;
//异常处理结束后,⼀定要将ExceptionHandled设置为true,否则仍然会继续抛出错误。
filterContext.ExceptionHandled = true;
}
}
}
当我们的⽅法中出现异常时,会将Views⽂件夹下Shared中的Error.cshtml页⾯返回到客户端,客户端页⾯不再显⽰成黄页,给⽤户⼀个良好的体验。
我们给Index⽅法添加上我们的异常处理器,再加⼀段引发异常的代码看下效果。
打开Index视图:
视图中显⽰的是Error页⾯中的内容。
通常我们⽤异常处理器来记录我们的程序异常⽇志,或者在产⽣异常时给客户端返回⼀个友好的提⽰内容。
过滤器的使⽤⽅法
在上⽂的⼀些例⼦中,我们把过滤器的特性都定义在了Action⽅法上,其实过滤器不仅可以应⽤在Action⽅法中,还可在应⽤在Controller和全局配置中。
应⽤在Controller中的使⽤⽅法和Action⼀直,在Controller类名上⽅添加特性标签即可。如图:
全局环境下的过滤器,则需要注册到FilterConfig⽂件中,例如MVC默认给我们注册的HandleErrorAttribute异常处理器:
并且我们可以注册很多个过滤器在全局环境下,那么在此注册的过滤器会应⽤到整个应⽤程序当中。
总结
本章对过滤器的类型,作⽤,定义以及使⽤⽅法做了⼀些说明,当然这些都是⽐较基础的内容,真正深⼊的理解还得多多使⽤,如果⽂章中有错误或者不⾜的地⽅,请⼤家在评论中指正出来。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论