• Controller Extensibility in ASP.NET MVC


          在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 

  • 相关阅读:
    第五次作业
    第四次作业
    第三次
    request.getAttribute()和 request.getParameter()有何区别
    .Servlet API中forward()与redirect()的区别
    jsp和servlet的区别、共同点、各自应用的范围
    Servlet的生命周期
    如何从CDN加载jQuery
    window.onload()函数和jQuery中的document.ready()的区别
    jquery中$.get()提交和$.post()提交的区别
  • 原文地址:https://www.cnblogs.com/Langzi127/p/2745732.html
Copyright © 2020-2023  润新知