过滤器(一)
一直在想,怎么写这个源码分析,那些大神们是如何一步步分析写的文章。最开始不可能就了解其本质实现的,我们对任何事物总是有个认知过程的的。那门我就从这个认知过程一步步的解析一下过滤器使用和其本质实现吧。当然前提还是对Mvc有一些了解。(这里我装了ReSharp插件和Reflector一起使用的,所以有些源码格式可能不太一样)
1.初识过滤器,
往往认识事物的开始就是现象,认识了解一个方法或者一个类,也一般都是从其实例开始的。
需求:做电商的网站,那么要有用户的注册,登陆等。访问某个页面也不也说谁都能够访问的,别如我们的个人信息,购买信息等等。那肯定就需要对用户进行验证过滤。那就就有了这个需求,我们的Action需要用户过滤。简而言之,我们要在Action执行之前,进行过滤(执行自定义的代码)。
那么,第一个demo就来了。(可能是从书本上看来的,可能是网上查找的,可能是老师教的。不管怎么样,你肯定已经见过了几个类似的demo了)。
/// <summary> /// 自定义用户登录特性 /// </summary> public class UserLoginInfoAttribute : ActionFilterAttribute { /// <summary> /// 在Action执行之前 /// </summary> /// <param name="filterContext"></param> public override void OnActionExecuting(ActionExecutingContext filterContext) { //这里是判断逻辑 } }
而且你肯定应该知道了,这个方法就是在Action执行之前执行的(即便你没有根据名称才出来,也应该msdn查出来吧),ok。这确实能够完成我们的需求,那么有这么个override的方法,根据经验,看样子应该还有Action执行之后的方法才对,根据智能提示看一下吧,哎呦我去,果然有,还有意外收获。OnActionExecuting,OnActionExecuted,OnResultExecuting,OnResultExecuted,嗯这个不能仅可以捕获Action执行前,Action执行后,还可以在Result返回前,返回后高一些东东,嗯,提供的挺全嘛。
有点经验的同学是不是就大概能猜到Action执行干了点什么了。可能就是类似的这么段代码吧。
/// <summary> /// Action的执行 /// </summary> public void ExcuteAction() { //1.OnActionExecuting //2.执行Action //3.OnActionExecuted //4.OnResultExecuting //5.生成结果 //6.OnResultExecuted }
我去,这都猜到了,那要不要证实下呢,嗯,还是证实下的好,要不总感觉欠人1毛钱似的,赶脚怪怪的。好吧,一起看一看吧
Action的执行,从哪里看呢,(如果同学看过些Mvc的相关知识应该就知道从哪里入手,不知道暂时也没什么),那就从Controller看看吧,别问我为什么,我也不知道,多年当猴(程序猿)进化的结果吧。(想想大概也能猜的到,Action是在Controller中的,那Action的执行最相关的估么就应该在Controller中有这么个执行的东西),转到定义看下吧。
public abstract class Controller : ControllerBase, IActionFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IController, IAsyncManagerContainer { // Fields private IActionInvoker _actionInvoker; //此处略去N行代码。。。() }
我去这么一大堆,尼玛完全亮瞎了我的钛合金狗眼了,估计也没有心情完全看下去,但是瞅两眼有没有让你眼前一亮的东西呢。IActionInvoker。微软起名还是很地道的。(如果你没看见或者一点都没看,我只能说呵呵),究其实现,其实是这么个方法CreateActionInvoker。尼玛看了看实现,又往下翻了翻定义什么的,没什么头绪,算了,看这样子,换个地方吧。在这之中你肯定应该看过了这么个东西
/// <summary> /// 定义操作调用程序的协定,该调用程序用于调用一个操作以响应 HTTP 请求。 /// </summary> public interface IActionInvoker { /// <summary> /// 使用指定的控制器上下文来调用指定操作。 /// </summary> /// /// <returns> /// 如果找到了指定操作,则为 true;否则为 false。 /// </returns> /// <param name="controllerContext">控制器上下文。</param><param name="actionName">操作的名称。</param> bool InvokeAction(ControllerContext controllerContext, string actionName); }
这就是控制Action执行的接口。看看Controller的定义。ControllerBase玩意是不是应该引起我们的关注呢。嗯,应该是Controller的基类吧。看看定义吧
public abstract class ControllerBase : IController { // Fields private DynamicViewDataDictionary _dynamicViewDataDictionary; private readonly SingleEntryGate _executeWasCalledGate; private TempDataDictionary _tempDataDictionary; private bool _validateRequest; private IValueProvider _valueProvider; private ViewDataDictionary _viewDataDictionary; // Methods protected ControllerBase(); protected virtual void Execute(RequestContext requestContext); protected abstract void ExecuteCore(); protected virtual void Initialize(RequestContext requestContext); void IController.Execute(RequestContext requestContext); internal void VerifyExecuteCalledOnce(); // Properties public ControllerContext ControllerContext { get; set; } public TempDataDictionary TempData { get; set; } public bool ValidateRequest { get; set; } public IValueProvider ValueProvider { get; set; } [Dynamic] public object ViewBag { [return: Dynamic] get; } public ViewDataDictionary ViewData { get; set; } }
还可以,比Controller简单很多,但是看一眼没什么收获。继续往下走
/// <summary> /// 定义控制器所需的方法。 /// </summary> public interface IController { /// <summary> /// 执行指定的请求上下文。 /// </summary> /// <param name="requestContext">请求上下文。</param> void Execute(RequestContext requestContext); }
哎,有点戏,Execute,执行请求上下文,应该会有Action的东西,毕竟我们的Action才是真正每次处理请求的真正内容么。
回到Controller看,我们会看到这么个方法
protected override void ExecuteCore() { // If code in this method needs to be updated, please also check the BeginExecuteCore() and // EndExecuteCore() methods of AsyncController to see if that code also must be updated. PossiblyLoadTempData(); try { string actionName = RouteData.GetRequiredString("action"); if (!ActionInvoker.InvokeAction(ControllerContext, actionName)) { HandleUnknownAction(actionName); } } finally { PossiblySaveTempData(); } }
我去,终于有点收获。ActionInvoker.InvokeAction(ControllerContext, actionName)这个是不是就是Action的执行呢。仔细一看,我去依赖接口调用的方法。我去,喷血了。(这个是我的软肋,这种依赖接口的调用,我就不会找其具体实现了该接口的代码的位置了,跪求大神指导。)。(当然如果你运气比较好直接用的reflector看的话,应该会有所发现,因为Controller和ControllerActionInvoker(这是我后面才知道的)的定义是挨着的。)这个是我之前,学习大神Artech的文章看到的。这里曲折的寻找之路大家就只能各自发挥了,我这里就直接给出ControllerActionInvoker的InvokeAction执行的。看下代码片段
public class ControllerActionInvoker : IActionInvoker { 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; } }
恩恩,大概看看,应该能够找到这么两个方法InvokeActionMethodWithFilters,InvokeActionResultWithFilters,这尼玛也太容易了,这方法名不就写的很清楚了么。(让我们体验了一把见名知意的重要性)。继续看一看,最后看到这么两个方法InvokeActionMethodFilter,InvokeActionResultFilter
internal static ActionExecutedContext InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func<ActionExecutedContext> continuation) { filter.OnActionExecuting(preContext); if (preContext.Result != null) { return new ActionExecutedContext(preContext, preContext.ActionDescriptor, true /* canceled */, null /* exception */) { Result = preContext.Result }; } bool wasError = false; ActionExecutedContext postContext = null; try { postContext = continuation(); } 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. postContext = new ActionExecutedContext(preContext, preContext.ActionDescriptor, false /* canceled */, null /* exception */); filter.OnActionExecuted(postContext); throw; } catch (Exception ex) { wasError = true; postContext = new ActionExecutedContext(preContext, preContext.ActionDescriptor, false /* canceled */, ex); filter.OnActionExecuted(postContext); if (!postContext.ExceptionHandled) { throw; } } if (!wasError) { filter.OnActionExecuted(postContext); } return postContext; } internal static ResultExecutedContext InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func<ResultExecutedContext> continuation) { filter.OnResultExecuting(preContext); if (preContext.Cancel) { return new ResultExecutedContext(preContext, preContext.Result, true /* canceled */, null /* exception */); } bool wasError = false; ResultExecutedContext postContext = null; try { postContext = continuation(); } 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. postContext = new ResultExecutedContext(preContext, preContext.Result, false /* canceled */, null /* exception */); filter.OnResultExecuted(postContext); throw; } catch (Exception ex) { wasError = true; postContext = new ResultExecutedContext(preContext, preContext.Result, false /* canceled */, ex); filter.OnResultExecuted(postContext); if (!postContext.ExceptionHandled) { throw; } } if (!wasError) { filter.OnResultExecuted(postContext); } return postContext; }
嗯,看方法第一行和最有一行,好了大概跟我们猜的差不多,只不过实现要比我们的想想复杂的多。我们猜测的一行注释基本上就省去了千百行的代码。从看的过程中,也能学到很多别的东西,微软的一些设计思路和方式。以后再看的话,是不是会容易一些呢。
过滤器的实现,是不是就是种面向切面的AOP思想呢,之前我也写过类似的东东,但是能不能提升到微软这个层次呢,至少从几十几百行代码一下子变成小几千行,是不是有种很牛逼的赶脚,呵呵,开个玩笑,代码还是要精简些的。AOP,不是很熟悉,要不要找篇文章再深入研究下呢,先把这个研究完再说吧。
面向抽象,依赖接口的编程,有木有。(如果不是的话,我就可以直接转到定义了,不需要花费精力找Action的执行了)
单例模式有木有。别说你没看到。(看这个名称貌似也是个内部Descriptor缓存类的东东)
确实学到不少东西。我去,我们要干嘛来着,研究过滤器的本质和实现。我去基本木有进展。
public class ControllerActionInvoker : IActionInvoker { private static readonly ControllerDescriptorCache _staticDescriptorCache = new ControllerDescriptorCache(); }
算了,尼玛一个问题折腾了半天,不过还好有些收获。休息休息,等待继续研究吧。周末得回家,估计不能发blog了,不过研究还是得继续,否则没法完整的贯通下来了。
注:1.文章中提到了(这种依赖接口的调用,我就不会找其具体实现了该接口的代码的位置了,跪求大神指导。)
2.由于个人水平问题,难免会有一些错误,忘大神支出共同进步。