代码改变世界

ASP.NET MVC Preview 2 - 流程分析 (2)

2008-04-30 13:17  Jacky_Xu  阅读(717)  评论(0编辑  收藏  举报
这次探险历程将从 MvcHandler 开始。
public class MvcHandler : IHttpHandler, IRequiresSessionState
{
  protected virtual void ProcessRequest(HttpContext httpContext)
  {
    HttpContextBase base2 = new HttpContextWrapper2(httpContext);
    this.ProcessRequest(base2);
  }

  protected internal virtual void ProcessRequest(HttpContextBase httpContext)
  {
    string requiredString = this.RequestContext.RouteData.GetRequiredString("controller");
    IControllerFactory controllerFactory = this.ControllerBuilder.GetControllerFactory();
    IController controller = controllerFactory.CreateController(this.RequestContext, requiredString);

    if (controller == null)
    {
      throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture,
        MvcResources.ControllerBuilder_FactoryReturnedNull,
        new object[] { controllerFactory.GetType(), requiredString }));
    }
    
    try
    {
      ControllerContext controllerContext = new ControllerContext(this.RequestContext, controller);
      controller.Execute(controllerContext);
    }
    finally
    {
      controllerFactory.DisposeController(controller);
    }
  }
}

首先从 RouteData 中提取 Controller 的名字(这个名字是我们在 Global.asax.cs RegisterRoutes 中注册 Route 时提供的),然后获取 ControllerFactory,只不过这里面专门提供了一个 ControllerBuilder。
internal ControllerBuilder ControllerBuilder
{
  get
  {
    if (this._controllerBuilder == null)
    {
      this._controllerBuilder = ControllerBuilder.Current;
    }
    return this._controllerBuilder;
  }
  set
  {
    this._controllerBuilder = value;
  }
}

除非我们自定义 MvcHandler,否则就直接去看看这个 ControllerBuilder.Current 吧。
public class ControllerBuilder
{
  // Fields
  private Func<IControllerFactory> _factoryThunk;
  private static ControllerBuilder _instance = new ControllerBuilder();

  // Methods
  public ControllerBuilder()
  {
    this.SetControllerFactory(new DefaultControllerFactory());
  }

  public IControllerFactory GetControllerFactory()
  {
    return this._factoryThunk();
  }

  public void SetControllerFactory(Type controllerFactoryType)
  {
    // ....

    this._factoryThunk = delegate
    {
      IControllerFactory factory;
      
      try
      {
        factory = (IControllerFactory)Activator.CreateInstance(controllerFactoryType);
      }
      catch (Exception exception)
      {
        // ...
      }

      return factory;
    };
  }

  public void SetControllerFactory(IControllerFactory controllerFactory)
  {
    // ...
    this._factoryThunk = () => controllerFactory;
  }

  // Properties
  public static ControllerBuilder Current
  {
    get { return _instance; }
  }
}

嗯,有点意思。剔除掉无关紧要的代码,这个 ControllerBuilder 告诉我们如下事实。

(1) 通常情况下,返回一个默认的 DefaultControllerFactory 实例。
(2) 我们可以在 Application_Start 中通过 ControllerBuilder.Current.SetControllerFactory 方法来注册一个我们自定义的工厂。
(3) 核心代码: factory = (IControllerFactory)Activator.CreateInstance(controllerFactoryType);

好了,回到前面的流程。MvcHandler.ProcessRequest 是通过 DefaultControllerFactory.CreateController(RequestContext, requiredString) 来返回 IController 实例。那么我们就看看这个 DefaultControllerFactory.CreateController 又有什么玄机。
public class DefaultControllerFactory : IControllerFactory
{
  protected internal virtual IController CreateController(RequestContext requestContext, string controllerName)
  {
    //...

    this.RequestContext = requestContext;
    Type controllerType = this.GetControllerType(controllerName);
    return this.GetControllerInstance(controllerType);
  }

  protected internal virtual Type GetControllerType(string controllerName)
  {
    Type type2;
    
    // ...

    if (!this.ControllerTypeCache.Initialized)
    {
      // ... 自己打开 Reflector 看吧 ...
    }

    if (!this.ControllerTypeCache.TryGetControllerType(controllerName, out type2))
    {
      return null;
    }
    
    // ...

    return type2;
  }

  protected internal virtual IController GetControllerInstance(Type controllerType)
  {
    IController controller;

    //...

    try
    {
      controller = Activator.CreateInstance(controllerType) as IController;
    }
    catch (Exception exception)
    {
      // ...
    }
    return controller;
  }
}

这个实在没啥好说的,通过反射来创建 Controller 实例。唯一有点看头的就是 GetControllerType 里面做了些缓存处理,以此来避免频繁使用反射造成的性能问题。回到 MvcHandler.ProcessRequest(),在得到控制器实例后,MvcHandler 开始了调用 Controller.Execute() 来进一步触发后续操作,同时对其上下文进一步封装,除了前面创建的 RequestContext,还加上了当前这个 Controller 对象的引用。
public class Controller : IController
{
  protected internal virtual void Execute(ControllerContext controllerContext)
  {
    //...

    string requiredString = this.RouteData.GetRequiredString("action");
    if (!this.InvokeAction(requiredString))
    {
      this.HandleUnknownAction(requiredString);
    }
  }
}

获取 Action 的名字,然后开始执行 InvokeAction。
protected internal bool InvokeAction(string actionName)
{
  return this.InvokeAction(actionName, new RouteValueDictionary());
}

protected internal virtual bool InvokeAction(string actionName, RouteValueDictionary values)
{
  // ...

  MemberInfo[] infoArray = base.GetType().GetMember(actionName, MemberTypes.Method, ...);
  MethodInfo methodInfo = null;
  
  foreach (MemberInfo info2 in infoArray)
  {
    MethodInfo info3 = (MethodInfo)info2;

    if ((!info3.IsDefined(typeof(NonActionAttribute), true) && !info3.IsSpecialName) &&
      info3.DeclaringType.IsSubclassOf(typeof(Controller)))
    {
      if (methodInfo != null)
      {
        throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture,
          MvcResources.Controller_MoreThanOneAction,
          new object[] { actionName, base.GetType() }));
      }
      methodInfo = info3;
    }
  }
  
  if (methodInfo != null)
  {
    this.InvokeActionMethod(methodInfo, values);
    return true;
  }

  return false;
}

这段代码还是很有趣的。首先,它通过反射获取所有同名 Action 方法信息;其次,它过滤掉所有有 NonActionAttribute 和 IsSpecialName 标记的方法;第三,当同名有效 Action 被重载时它会抛出异常 —— 提示 "Controller_MoreThanOneAction"。

也就是说下面的写法是可以的。
public class HomeController : Controller
{
  public void About()
  {
    About(123);
  }

  [NonAction]
  public void About(int x)
  {
    Response.Write("x...");
  }
}

可一旦去掉那个 "[NonAction]",你将看到如下的异常信息。
More than one action named 'About' was found on controller 'Rainsoft.Web.Controllers.HomeController'.

继续 InvokeActionMethod,这个方法看上去很复杂,其实最核心的代码就最后一行,前面都是相关参数的分解。
protected internal virtual void InvokeActionMethod(MethodInfo methodInfo, RouteValueDictionary values)
{
  // ...

  this.InvokeActionMethodFilters(methodInfo, delegate
  {
    methodInfo.Invoke(this, parameterValues);
  });
}

这行代码将 Action 的调用作为一个委托,连同反射信息传递给 InvokeActionMethodFilters。
private void InvokeActionMethodFilters(MethodInfo methodInfo, Action continuation)
{
  List<ActionFilterAttribute> filters = new List<ActionFilterAttribute>
  {
    new ControllerActionFilter(this)
  };

  Stack<MemberInfo> memberChain = new Stack<MemberInfo>();
  for (Type type = base.GetType(); type != null; type = type.BaseType)
  {
    memberChain.Push(type);
  }

  List<ActionFilterAttribute> collection = SortActionFilters(memberChain);
  filters.AddRange(collection);

  List<ActionFilterAttribute> list3 = PrepareMethodActionFilters(methodInfo);
  filters.AddRange(list3);
  
  FilterContext context = new FilterContext(this.ControllerContext, methodInfo);
  new ActionFilterExecutor(filters, context, continuation).Execute();
}

这个方法首先将默认的过滤器 ControllerActionFilter 加到列表,然后提取所有继承层次上基类的过滤器特性。最后将这些过滤器集合、过滤上下文,连同前一个方法传递进来的 Action 执行委托(continuation) 再次转交给了一个 ActionFilterExecutor 对象实例,并调用其 Execute 方法。
internal sealed class ActionFilterExecutor
{
  // Fields
  private FilterContext _context;
  private Action _continuation;
  private List<ActionFilterAttribute> _filters;

  // Methods
  public ActionFilterExecutor(List<ActionFilterAttribute> filters, FilterContext context, Action continuation)
  {
    this._filters = filters;
    this._context = context;
    this._continuation = continuation;
  }

  public void Execute()
  {
    IEnumerator<ActionFilterAttribute> enumerator = this._filters.GetEnumerator();
    this.ExecuteRecursive(enumerator);
  }

  private FilterExecutedContext ExecuteRecursive(IEnumerator<ActionFilterAttribute> enumerator)
  {
01:    if (enumerator.MoveNext())
02:    {
03:      ActionFilterAttribute current = enumerator.Current;
04:      FilterExecutingContext filterContext = new FilterExecutingContext(this._context);
05:      current.OnActionExecuting(filterContext);
06:
07:      if (filterContext.Cancel)
08:      {
09:        return new FilterExecutedContext(this._context, null);
10:      }
11:
12:      bool flag = false;
13:      FilterExecutedContext context2 = null;
14:      try
15:      {
16:        context2 = this.ExecuteRecursive(enumerator);
17:      }
18:      catch (Exception exception)
19:      {
20:        flag = true;
21:        context2 = new FilterExecutedContext(this._context, exception);
22:        current.OnActionExecuted(context2);
23:        if (!context2.ExceptionHandled)
24:        {
25:          throw;
26:        }
27:      }
28:
29:      if (!flag)
30:      {
31:        current.OnActionExecuted(context2);
32:      }
33:      return context2;
34:    }
35:
36:    this._continuation();
37:    return new FilterExecutedContext(this._context, null);
  }
}

ExecuteRecursive 看上去不大容易理解。不过对于习惯使用递归的人来说,也不是什么难事。

(1) 通过迭代器 MoveNext() 方法提取一个过滤器对象,执行其 OnActionExecuting 方法。

(2) 如果该方法设置了 filterContext.Cancel = true,则放弃后续执行代码。这种机制为我们提供了更好的控制(Preview 1 通过 Context.OnPreAction 的返回值来做同样的控制,现已废弃),比如写一个 CacheFilterAttribute,在缓存未过期时直接输出静态内容,并终止后续执行。(我会在后面章节,提供一个可用的缓存过滤器代码)

(3) 进入第 16 行递归调用,这样一来,就可以一层一层调用所有的 ActionFilterAttribute.OnActionExecuting 方法,直到 MoveNext() == false。

(4) 在最后一次递归调用时,由于 enumerator.MoveNext() == false,故 36 行的 _continuation() 方法得以被执行。还记得这个方法吗?就是前面给传递过来的 Action 方法委托,这意味着我们写的 Action 方法总算是执行了。这个方法是我们这一章流程分析的终点。

(5) 在 Action 委托执行完成后,递归调用自 29 行恢复,逐级往上回溯,直到最初那个方法堆栈。这样所有 ActionFilterAttribute.OnActionExecuted 也被执行完成。

好了,到此为止,我们基本完成了 "核心" 部分的流程分析过程。虽然简单了些,但对于我们理解 ASP.NET MVC 执行机制和原理还是很有必要的。下一章的分析,就得从那个 Action Delegate 开始了。

-----------------

附: 流程分析图

uploads/200803/11_193520_mvc2.png


查看大图