在Asp.net MVC中,一个request过来后,mvc framework是怎么处理的:

一个请求过来,经过Route系统的处理后, 它会找出适合request的controller和action的名称。注意,这个时候仅仅是找到了它的名称,那它怎么去创建对应的Controller,并调用action呢?

 

      1. Controller Factory:

      所有的Controller Factory都要实现IControllerFactory接口:

    public interface IControllerFactory
    {
        IController CreateController(RequestContext requestContext, string controllerName);
        SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName);
        void ReleaseController(IController controller);
    }

才能被注入到MVC Framework中。 标黄的部分就是上文问题的答案主体了。好,我们先自己实现一个:

    public class CustomControllerFactory : IControllerFactory
    {
        public IController CreateController(RequestContext requestContext, string controllerName)
        {
            Type targetType = null;
            switch (controllerName)
            {
                case "Home":
                    //requestContext.RouteData.Values["controller"] = "First";
                    requestContext.RouteData.Values["controller"] = "Home";
                    targetType = typeof(FirstController);
                    break;
                case "First":
                    requestContext.RouteData.Values["controller"] = "First";
                    targetType = typeof(FirstController);
                    break;
                case "Second":
                    requestContext.RouteData.Values["controller"] = "Second";
                    targetType = typeof(FirstController);
                    break;
            }

            return targetType == null ? null : Activator.CreateInstance(targetType) as IController;
        }

        public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
        {
            return SessionStateBehavior.Default; 
        }

        public void ReleaseController(IController controller)
        {
            var disposeableController = controller as IDisposable;
            if (disposeableController != null)
            {
                disposeableController.Dispose();
            }
        }
    }

在CreateController方法中,根据名称选择目标controller类型,然后通过Activator反射构造出一个controller实例对象。关于GetControllerSessionBehavior 方法,是返回session的状态设置,后边有更详细描述。 ReleaseController方法通过转换为IDisposable,巧妙释放controller资源。 另外,可以看到,在CreateController方法中,你甚至可以篡改所返回的controller对象(被注释那部分)。

      构造完了,就是注入使用了。在Application_Start方法中,加入:

   ControllerBuilder.Current.SetControllerFactory(new CustomControllerFactory());

再看ControllerBuilder的定义:

    public class ControllerBuilder
    {
        public ControllerBuilder();
        public static ControllerBuilder Current { get; }
        public HashSet<string> DefaultNamespaces { get; }
        public IControllerFactory GetControllerFactory();
        public void SetControllerFactory(IControllerFactory controllerFactory);
        public void SetControllerFactory(Type controllerFactoryType);
    }

你就知道,一个MVC项目中只能同时启用一个Controller Factory了。所以,通常情况下,不建议自己实现它。另外,基于ControllerBuilder的定义,可以定义全局优先的Route namespace:

    ControllerBuilder.Current.DefaultNamespaces.Add("ControllerExtensiblity.Controllers");
    ControllerBuilder.Current.DefaultNamespaces.Add("MyProject.*");

http://www.cnblogs.com/Langzi127/archive/2012/10/20/2732725.html 中描述的那些方法不一样的是,上述2个namespace对所有route配置都取作用。不过,相同的一点就是,如果两者同时匹配到指定route,都会报异常。

      现在研究一下DefaultControllerFactory的定义:

    public class DefaultControllerFactory : IControllerFactory
    {
        public DefaultControllerFactory();
        public DefaultControllerFactory(IControllerActivator controllerActivator);

        #region IControllerFactory Members

        public virtual IController CreateController(RequestContext requestContext, string controllerName);
        public virtual void ReleaseController(IController controller);
        SessionStateBehavior IControllerFactory.GetControllerSessionBehavior(RequestContext requestContext, string controllerName);

        #endregion

        protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType);
        protected internal virtual SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, Type controllerType);
        protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName);
    }

可以看到,它还可以通过注入IControllerActivator对象来达到自己的目的。而IControllerActivator的定义为:

    public interface IControllerActivator
    {
        IController Create(RequestContext requestContext, Type controllerType);
    }

所以,实际上它才是controller的创建者。我们先来自己实现一下它:

    public class CustomControllerActivator : IControllerActivator
    {
        public IController Create(RequestContext requestContext, Type controllerType)
        {
            if(controllerType == typeof(FirstController))
            {
                controllerType = typeof (SecondController);
                requestContext.RouteData.Values["controller"] = "Second";
            }

            return DependencyResolver.Current.GetService(controllerType) as IController;
        }
    }

然后简单定义一个ControllerFactory,并使用上述ControllerActivator:

    public class CustomControllerFactory2 : DefaultControllerFactory
    {
        public CustomControllerFactory2(IControllerActivator controllerActivator) : base(controllerActivator)
        {
            
        }
    }

最后注册启用:

    ControllerBuilder.Current.SetControllerFactory(new CustomControllerFactory2(new CustomControllerActivator()));

 

      2. Action Invoker

      通过ControllerActivator,已经可以创建出目标controller对象实例了。那么通过controller对象和action名称(仅仅是一个字符串而已),怎么来调用action方法?看IActionInvoker的定义:

    public interface IActionInvoker
    {
        bool InvokeAction(ControllerContext controllerContext, string actionName);
    }

对细节还是不很清楚。还是自己先实现一个吧:

    public class CustomActionInvoker : IActionInvoker
    {
        public bool InvokeAction(ControllerContext controllerContext, string actionName)
        {
            if(actionName == "Index")
            {
                controllerContext.HttpContext.Response.Write("This is output from the Index action.");
                return true;
            }

            return false;   //http code: 404
        }
    }

这里当然太简单直接了,如果action名称为Index,直接Response Write了,否则就404. 使用方式: 

    public class CustomActionInvokerController : Controller
    {
        public CustomActionInvokerController()
        {
            this.ActionInvoker = new CustomActionInvoker();
        }

        public ActionResult Index()
        {
            return View();
        }
    }

在MVC中,它是通过ControllerActionInvoker类来做这个事情,之后再详细研究它。

       注意:ActionInvoker调用action时,不仅仅是要核对方法的签名,还要核对它头上的ActionMethodSelector(及其子类)特性,如:ActionName 、 HttpPost、NonAction等。

       关于ActionName,可以用于别名中需要包含非法字符,如:

    [ActionName("User-Register")]
    public ActionResult UserRegister()

调用的时候,通过User-Register即可。也可用于实现REST:

    public class StaffController : Controller
    {
        [HttpGet]
        [ActionName("Staff")]
        public ActionResult StaffGet(int id)
        {
            // logic to get staff
            return Content("got staff");
        }

        [HttpPost]
        [ActionName("Staff")]
        public ActionResult StaffModify(int id, StaffMember person)
        {
            // ... logic to modify or create data item
            return Content("modifed the staff");
        }

        [HttpDelete]
        [ActionName("Staff")]
        public ActionResult StaffDelete(int id)
        {
            // ... logic to delete data item
            return Content("deleted the staff");
        }
    }

调用方式,如:

@using(Html.BeginForm("Staff""Staff"new{id=1})){
    @Html.HttpMethodOverride(HttpVerbs.Delete)
    <input type="submit" value="Delete" />
}

标黄部分是为了fix html form不支持delete。

      除了上述ActionMethodSelector特性,还可以自己创建它的拓展类:

    public class LocalAttribute : ActionMethodSelectorAttribute
    {
        public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
        {
            return controllerContext.HttpContext.Request.IsLocal;
        }
    }

使用方式:

    [Local]
    public ActionResult About()
    {
        return View();
    }

这样就只有本地用户可以看到about action了。

 

      3. Performance Improvement

      基于上述的过程,要做性能优化,这里列举2个方法:

      1) 尽量少使用Session,我的意思是将Controller的Session状态设置为ReadOnly或者Disabled;

      2) 使用异步action。

当然还有其他一些办法,但这里仅列举这2种。假如我们要实现一个Data的action,并且异步:

    public class RemoteDataController : AsyncController
    {
        public void DataAsync()
        {
            AsyncManager.OutstandingOperations.Increment();
            Task.Factory.StartNew(() =>
                                      {
                                          //Thread.Sleep(2000);
                                          
//AsyncManager.Parameters["result"] = "Finally done the work.";
                                          
//AsyncManager.OutstandingOperations.Decrement();

                                          WebRequest req = WebRequest.Create("http://www.asp.net");
                                          req.BeginGetResponse((IAsyncResult ias) =>
                                          {
                                              WebResponse resp = req.EndGetResponse(ias);
                                              string content = new StreamReader(resp.GetResponseStream()).ReadToEnd();
                                              AsyncManager.Parameters["result"] = content;
                                              AsyncManager.OutstandingOperations.Decrement();
                                          }, null);
                                      });

            //http://baike.baidu.com/view/1256215.htm
        }

        public ActionResult DataCompleted(string result)
        {
            //return Content(result);

            return Content(result, "text/html");
        }
    }

可以看到action方法一拆为二,分为:DataAsync、DataCompleted。而它们之间是通过AsyncManager来连接和传值的。 关于异步的实现是利用windows内核的IOCP,具体见http://baike.baidu.com/view/1256215.htm 。

 

源码download