normalian blog

Let's talk about Microsoft Azure, ASP.NET and Java!

ASP.NET MVCの処理(Controller、Viewの実行される場所について)

ASP.NET MVCが処理される流れとして、ちょっと御幣がありますが、まずはSystem.Web.Mvc.MvcHandler#ProcessRequest内で以下な感じで実行される。

    public class MvcHandler : IHttpHandler, IRequiresSessionState {
        protected internal virtual void ProcessRequest(HttpContextBase httpContext) {
            AddVersionHeader(httpContext);
            // Get the controller type
            string controllerName = RequestContext.RouteData.GetRequiredString("controller");

            // Instantiate the controller and call Execute
            IControllerFactory factory = ControllerBuilder.GetControllerFactory();
            IController controller = factory.CreateController(RequestContext, controllerName);
            if (controller == null) {
                throw new InvalidOperationException(
                    String.Format(
                        CultureInfo.CurrentUICulture,
                        MvcResources.ControllerBuilder_FactoryReturnedNull,
                        factory.GetType(),
                        controllerName));
            }
            try {
                controller.Execute(RequestContext);
            }
            finally {
                factory.ReleaseController(controller);
            }
    }

見て察せられるとおり、factory.CreateController内でリフレクション使ってコントローラ作ってます。デフォルトでは「System.Web.Mvc.DefaultControllerFactory」が登録されてまして、この子を読むと内部操作がわかります。


次に、System.Web.Nvc.Controller#Excecute内の中身なのですが、

public abstract class ControllerBase : MarshalByRefObject, IController {
        (中略)
        protected virtual void Execute(RequestContext requestContext) {
            if (requestContext == null) {
                throw new ArgumentNullException("requestContext");
            }

            Initialize(requestContext);
            ExecuteCore();
        }
        (中略)
}

public abstract class Controller : ControllerBase, IActionFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter {
        (中略)
        protected override void ExecuteCore() {
            TempData.Load(ControllerContext, TempDataProvider);

            try {
                string actionName = RouteData.GetRequiredString("action");
                if (!ActionInvoker.InvokeAction(ControllerContext, actionName)) {
                    HandleUnknownAction(actionName);
                }
            }
            finally {
                TempData.Save(ControllerContext, TempDataProvider);
            }
        }
        (中略)
}

内部でわかる通り、デフォルトではControllerBase#Excecuteが実行されて、そいつからController#ExcuteCoreが呼び出されます。メソッド内部で呼び出されるActionInvoker#InvokeActionがアクション実行、フィルター実行、レンダー実行をすべて握っていたりします。
ActionInvokerはControllerから指定されたactionNameに対応するメソッドをリフレクションを使って実行し、各種フィルタを実行します。次に、ActionResult(通常はViewResult)を用いてResponseに対する後処理(IView#Render)を実行します。(後で、その辺りもまとめるとしよう)

一応、ビューが実行されるまでの流れ自体は書いておこう(備忘録的な意味で)。

  1. ControllerActionInvoker#InvokeAction
  2. ControllerActionInvoker#InvokeActionResultWithFilters(例外が発生した場合は直にControllerActionInvoker#InvokeActionResult)
  3. ControllerActionInvoker#InvokeActionResult
  4. ActionResult#ExecuteResult
  5. (ViewResultの場合は)ViewResultBase#ExecuteResult
  6. (FindViewを実行されて)View#Render

とかになってる。この辺りの流れさえわかってると、ASP.NET MVCで何が起こってるかは大体追えると思う。