一个Action Filter是一个特性(Attribute),定义了横切在Action方法执行前后的行为。一个Action Filter可以应用在某个Action方法上,也可以应用在整个Controller上。

  所有Action Filter必须继承自FilterAttribute,同时必须实现至少一个以下接口:

  IAuthorizationFilter,负责认证和授权,包含一个OnAuthorization方法。

  IActionFilter,在Action方法执行前后调用,包含OnActionExecuting和OnActionExecuted两个方法。

  IResultFilter,在ActionResult执行前后调用,包含OnResultExecuting和OnResultExecuted两个方法。

  IExceptionFilter,当发生异常时调用,不管是在Action方法中、Result执行中或是前面三种Filter的执行过程中产生的未处理异常都会触发IExceptionFilter的OnException方法。

  以上几种ActionFilter的执行顺序就是按照上面的顺序,相同种类的ActionFiler按顺序执行。接口中的配对方法需要配对执行。

  ASP.Net MVC内建了几个ActionFilter供我们使用:

  ActionFilterAttribute,实现了IActionFilter和IResultFilter,但是方法内部什么也不做,供继承它的子类重写。

  OutputCacheAttribute,缓存Action的执行输出结果,它继承了ActionFilterAttribute,又实现了IExceptionFilter接口。

  HandleErrorAttribute,实现了IExceptionFilter,供开发人员选择一个View来处理Exception

  AuthorizeAttribute,实现了IAuthorizationFilter,提供认证和授权的功能,开发人员可以选择继承它然后覆盖它的方法来实现自己的认证和授权机制。

  下面我打算结合源码深入理解一下这些Filters。

  ControllerActionInvoker类负责Action方法的调用,它的InvokeAction方法显示了Action方法调用,ActionResult执行和Filter之间的关系。

public virtual bool InvokeAction(ControllerContext controllerContext, string actionName) {if (controllerContext == null) {throw new ArgumentNullException("controllerContext");            }if (String.IsNullOrEmpty(actionName)) {throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");            }

            ControllerDescriptor controllerDescriptor = GetControllerDescriptor(controllerContext);            ActionDescriptor actionDescriptor = FindAction(controllerContext, controllerDescriptor, actionName);if (actionDescriptor != null) {                FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor);

try {                    AuthorizationContext authContext = InvokeAuthorizationFilters(controllerContext, filterInfo.AuthorizationFilters, actionDescriptor);if (authContext.Result != null) {// the auth filter signaled that we should let it short-circuit the request                        InvokeActionResult(controllerContext, authContext.Result);                    }else {if (controllerContext.Controller.ValidateRequest) {                            ValidateRequest(controllerContext);                        }

                        IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor);                        ActionExecutedContext postActionContext = InvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters);                        InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters, postActionContext.Result);                    }                }catch (ThreadAbortException) {// This type of exception occurs as a result of Response.Redirect(), but we special-case so that// the filters don't see this as an error.                    throw;                }catch (Exception ex) {// something blew up, so execute the exception filters                    ExceptionContext exceptionContext = InvokeExceptionFilters(controllerContext, filterInfo.ExceptionFilters, ex);if (!exceptionContext.ExceptionHandled) {throw;                    }                    InvokeActionResult(controllerContext, exceptionContext.Result);                }

return true;            }

// notify controller that no method matched            return false;        }

  这段代码先获取了Controller、Action、Filter的描述信息

  ControllerDescriptor controllerDescriptor = GetControllerDescriptor(controllerContext);

  ActionDescriptor actionDescriptor = FindAction(controllerContext, controllerDescriptor, actionName);

  FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor);

  接着使用这些描述信息,在恰当的时候先后调用了AuthorizationFilters、ActionFilters、ResultFilters、ExceptionFilters,几个关键方法是:

  AuthorizationContext authContext = InvokeAuthorizationFilters(controllerContext, filterInfo.AuthorizationFilters, actionDescriptor);//调用AuthorizationFilters

  ActionExecutedContext postActionContext = InvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters);//执行Action方法,同时调用ActionFilters

  InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters, postActionContext.Result); //执行ActionResult,同时调用ResultFilters

  ExceptionContext exceptionContext = InvokeExceptionFilters(controllerContext, filterInfo.ExceptionFilters, ex);//如果上面的过程中抛出未处理的异常,调用ExceptionFilter

  阅读这些代码,就可以详细了解Action和Filter的执行过程,我总结几点。

  1. 调用AuthorizationFilters

  ControllerActionInvoker.InvokeAuthorizationFilters方法依次调用每个注册的AuthorizationFilter的OnAuthorization方法,一旦发现在某个OnAuthorization方法调用过程中设置了ActionResult,就会停止继续执行,并且跳过Action方法,直接执行这个ActionResult,返回结果给客户端。如果在OnAuthorization方法中抛出未捕获异常,会直接跳至ExceptionFilters。

  2. 执行Action方法,同时调用ActionFilters

  ControllerActionInvoker.InvokeActionMethodWithFilters方法负责这个过程。

  

  我们先考虑只有一个ActionFilter的情况。

  首先执行ActionFilter的OnActionExecuting方法,如果在方法中设置了传入的上下文的Result属性,执行将被短路,直接返回这个Result,Action方法和ActionFilter的OnActionExecuted方法都不会被执行。

  如果ActionFilter的OnActionExecuting方法抛出异常,将直接跳转至ExceptionFilters。

  如果ActionFilter的OnActionExecuting方法正常返回且未设置Result属性,接下来执行Action方法,如果正常返回一个ActionResult,就将这个Result传递给ActionFilter的OnActionExecuted方法(作为上下文参数的Result属性)。如果Action方法中抛出异常,仍然会调用ActionFilter的OnActionExecuted方法,这时传给该方法的上下文对象的Result属性为null,Exception属性为Action方法抛出的异常对象,如果这时在OnActionExecuted方法中设置了上下文的ExceptionHandled为true,该异常不会再抛出,ExceptionHandled针对的是Action方法抛出的异常,如果是在OnActionExecuted方法的执行内部抛出异常,不管有没有设置ExceptionHandled为true都会抛出该异常,流程跳转至ExceptionFilters。

  

  现在我们考虑有多个ActionFilter的情况,假设有FilterA和FilterB,调用流程如下图

  执行FilterB的情况与只有一个ActionFilter的情形一样,前面已经讨论过了。在执行FilterA的方法时,FilterA将FilterB和Action方法看成一个整体,如果这个整体抛出异常,FilterA.OnActionExecuted方法的上下文中的Exception属性会被设为这个异常供我们处理,就像FilterB.OnActionExecuted方法的上下文中可能包含Action方法执行时抛出的异常一样。

  由此看出FilterA的执行和FilterB的执行的内部逻辑是一样的,就像同一个函数被嵌套着调用,ASP.Net MVC 源代码巧妙的使用了linq中的聚合方法Aggregate进行多个Filter的迭代,使用多层嵌套的lambda表达式来表示迭代的结果,迭代的结果是一个lambda表达式,即一个函数入口指针,指向最外面的Filter的执行起点。

protected virtual ActionExecutedContext InvokeActionMethodWithFilters(ControllerContext controllerContext, IList<IActionFilter> filters, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters) {            ActionExecutingContext preContext = new ActionExecutingContext(controllerContext, actionDescriptor, parameters);            Func<ActionExecutedContext> continuation = () =>new ActionExecutedContext(controllerContext, actionDescriptor, false /* canceled */, null /* exception */) {                    Result = InvokeActionMethod(controllerContext, actionDescriptor, parameters)                };

// need to reverse the filter list because the continuations are built up backward            Func<ActionExecutedContext> thunk = filters.Reverse().Aggregate(continuation,                (next, filter) => () => InvokeActionMethodFilter(filter, preContext, next));return thunk();        }

  

  3. 执行ActionResult,同时调用ResultFilters

  ControllerActionInvoker.InvokeActionResultWithFilters方法负责这个过程,考虑只有一个ResultFilter的情况,首先执行Filter的OnResultExecuting方法,在这个方法中可以重置ActionResult。如果在这个方法内将上下文的Cancel属性设为true,将停止ActionResult的执行,也就是说不会再执行ActionResult. ExecuteResult和Filter的OnResultExecuted方法,浏览器得不到返回。如果在OnResultExecuting方法内抛出异常,流程会跳转至ExceptionFilters。

  接着执行ActionResult,即调用该ActionRsult的ExecuteResult方法,向浏览器输出结果,如果这时抛出异常,会将这个异常先报告给Filter的OnResultExecuted方法,OnResultExecuted方法内部可以检查上下文的Exception属性,如果不为null,可以处理这个异常,如果不需要继续抛出,可以设置上下文的ExceptionHandled属性为true。

  最后执行Filter的OnResultExecuted方法。

  多个ResultFilter的情况与上面讨论多个ActionFilter的情况类似,就不赘述了。

  4. 如果上面的过程中抛出未处理的异常,调用ExceptionFilter

  ControllerActionInvoker.InvokeExceptionFilters方法负责这个过程。前面的Filters或Action方法执行抛出的异常都会被ExceptionFilter捕获到,可以在这里统一处理异常。InvokeExceptionFilters方法会依次调用每一个ExceptionFilter的OnException方法,如果在OnException方法中设置了上下文的Result属性并且将上下文的ExceptionHandled属性设为true,就会执行这个ActionResult输出内容给浏览器,一般会返回一个友好的错误页面,如果不设置ExceptionHandled为true,会继续抛出异常,最终用户看到异常信息的黄页。

  ASP.Net MVC 自带了一个好用的ExceptionFilter:HandleErrorAttribute,可以应用在Action方法上,它包含三个属性:ExceptionType(处理的异常类型,默认为typeof(Exception)),Master(母版页名称),View(视图名称,默认为“Error”),HandleError的OnException方法会给上下文返回一个指向属性中设置的View和Master名称的ViewResult,同时将ControllerName,ActionName还有Exception对象作为视图的Model对象的属性传给这个视图,视图的Model对象的类型为HandleErrorInfo类型,它包括ControllerName、ActionName、Exception三个属性。

  ASP.Net MVC 还提供了两个重要的Filter实现:

  AuthorizeAttribute:用于用户认证和授权

  OutputCacheAttribute:用于缓存Action执行结果

  我会在下一篇文章中详细介绍它们。

转载于:https://www.cnblogs.com/jinsikui/archive/2012/01/30/2327593.html

ASP.NET MVC3 Action Filters详解(一)相关推荐

  1. Asp.Net MVC3 简单入门详解过滤器Filter

    为什么80%的码农都做不了架构师?>>>    前言 在开发大项目的时候总会有相关的AOP面向切面编程的组件,而MVC(特指:Asp.Net MVC,以下皆同)项目中不想让MVC开发 ...

  2. ASP.NET2.0 ReportingServices使用详解

    ASP.NET2.0 ReportingServices使用详解 作者:清清月儿 主页:http://blog.csdn.net/21aspnet/          时间:2007.4.9 本文先做 ...

  3. Asp.net中GridView使用详解(引)【转】

    Asp.net中GridView使用详解(引) GridView无代码分页排序 GridView选中,编辑,取消,删除 GridView正反双向排序 GridView和下拉菜单DropDownList ...

  4. 【转】ASP.NET验证控件详解(非空验证,比较验证,范围验证,正则表达式,自定义验证)...

    [转]ASP.NET验证控件详解(非空验证,比较验证,范围验证,正则表达式,自定义验证) ASP.NET验证控件详解 现在ASP.NET,你不但可以轻松的实现对用户输入的验证,而且,还可以选择验证在服 ...

  5. ASP.NET MVC Action Filters

    在看这篇Fun with Http Headers in ASP.NET MVC Action Filters的时候,提到了 Roni Schuetz在codeplex上创建的一个项目ASP.NET ...

  6. ASP.NET Core 中 HttpContext 详解与使用 |

    ASP.NET Core 中 HttpContext 详解与使用 | Microsoft.AspNetCore.Http 详解 笔者没有学 ASP.NET,直接学 ASP.NET Core ,学完 A ...

  7. oracle中datepart函数,Asp DatePart 函数的语法详解(用于计算日期并返回指定的时间间隔)...

    Asp DatePart 函数的语法详解(用于计算日期并返回指定的时间间隔) 更新时间:2012年07月31日 21:32:58   作者: ASP(VBScript) 参考手册中,已经对 DateP ...

  8. Pinia中action使用详解

    actions的使用 动作相当于组件中的方法.它们可以使用actionsin 属性进行定义. 并且在pinia中的action既可以有同步函数也可以有异步函数. 在actions中可以通过this访问 ...

  9. Asp.Net中MVC缓存详解

    本文通过介绍了Asp.Net中MVC缓存的种类,以及他们之间的区别等内容,让学习者能够深入的了解MVC缓存的原理机制,以下是具体内容: 缓存是一种保存资源副本并在下次请求时直接使用该副本的技术.当 w ...

最新文章

  1. 构建DHCP及中继服务器
  2. Java加密与解密笔记(三) 非对称加密
  3. zookeeper学习总结
  4. matlab rbf函数_基于径向基函数(RBF)的无网格伪谱法与程序实现(2)——微分矩阵...
  5. Linux下profile和bashrc四种的区别
  6. ML之回归预测:机器学习中的各种Regression回归算法、关键步骤配图
  7. java 读取resources_java读取Resources下文件
  8. css线条伸缩_CSS3弹性伸缩布局(一)——box布局
  9. 利用marked.js写个简单Markdown编辑器(1)
  10. 计算机图形学跳一跳_微信小游戏“跳一跳”对内部审计师的启示
  11. 学习python: x+=1 与 x = x + 1
  12. 快速入门分布式消息队列之 RabbitMQ(2)
  13. 欧姆龙编程软件SysmacStudio卸载方法
  14. 三线表里加小短线_三线表的格式
  15. 服务器来料检测作用,元器件的来料检测需要检测哪些方面
  16. 【AI视野·今日CV 计算机视觉论文速览 第243期】Thu, 14 Apr 2022
  17. linux生成4g文件,linux中创建超过4g文件的方法-o_largefile?
  18. MATLAB绘制地形图和等高线图
  19. html采集插件如何用,火车采集器插件功能详解
  20. 阿里云容器服务全线升级,ACK Pro开启公测、边缘容器商业化

热门文章

  1. 李迟2021年11月知识总结
  2. Golang实践录:工程管理
  3. Linux C简单日志打印代码示例
  4. 递归的使用不当 导致 压缩文件不能压缩二级目录
  5. 【Elasticsearch】使用真实内存断路器提高节点弹性
  6. 【Kafka】kafka消费者参数
  7. 【Spring】Spring 中的bean 和我们java中的bean有什么区别以及spring 模拟实现
  8. 【MySQL】MySQL 8 PROCEDURE ANALYSE命令使用
  9. 【Spring】Spring Boot 支持 Https
  10. 【kubernetes】http proxy error stream error stream ID 3 INTERNAL_ERROR