zoukankan      html  css  js  c++  java
  • MVC源码分析

    上一篇, 出现了一个至关重要的类:MvcHandler, 接下来就来看一下MvcHandler吧. 先不看具体方法, 先看一下类里面的情况.

    //这里实现了两个重要的接口, 异步处理和同步处理的接口
    public
    class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState { // Fields private ControllerBuilder _controllerBuilder; private static readonly object _processRequestTag; internal static readonly string MvcVersion; public static readonly string MvcVersionHeaderName; // Methods static MvcHandler(); public MvcHandler(RequestContext requestContext); protected internal virtual void AddVersionHeader(HttpContextBase httpContext); protected virtual IAsyncResult BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, object state); protected internal virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state); protected internal virtual void EndProcessRequest(IAsyncResult asyncResult); private static string GetMvcVersionString(); protected virtual void ProcessRequest(HttpContext httpContext); protected internal virtual void ProcessRequest(HttpContextBase httpContext); private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory); private void RemoveOptionalRoutingParameters(); IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData); void IHttpAsyncHandler.EndProcessRequest(IAsyncResult result); void IHttpHandler.ProcessRequest(HttpContext httpContext); // Properties internal ControllerBuilder ControllerBuilder { get; set; } public static bool DisableMvcResponseHeader { get; set; } protected virtual bool IsReusable { get; } public RequestContext RequestContext { get; private set; } bool IHttpHandler.IsReusable { get; } }

    从上面看, 有两种执行方式, 一种是同步的, 一种是异步的. 那默认情况下, 其实会走异步的方式. 但是这里呢, 我想用同步的方式去分析, 其实过程原理都是一样的, 只是方式不同.

    一、解析

    注意到这个类, 实现了三个接口, 那第三个接口是干啥的呢? 先看一下这个接口的内容.

    public interface IRequiresSessionState
    {
    }

    点进去, 发现这个接口没有任何方法, 那么他是干什么的呢? 

    其实他是一种标志, 或者叫标记, 表示这个类有对Session的访问权限, 包括读写. 如果你想自定义Http处理程序, 又想操作Session的话, 记得实现这个接口.

    接下来, 回归正题了.  来看一下这里的 PR 方法.

    protected virtual void ProcessRequest(HttpContext httpContext)
    {
        HttpContextBase base2 = new HttpContextWrapper(httpContext);
        this.ProcessRequest(base2);
    }
    
    protected internal virtual void ProcessRequest(HttpContextBase httpContext)
    {
        IController controller;
        IControllerFactory factory;
        this.ProcessRequestInit(httpContext, out controller, out factory);
        try
        {
            controller.Execute(this.RequestContext);
        }
        finally
        {
            factory.ReleaseController(controller);
        }
    }

    这里也就只有三个方法了, 我们一个一个分析.

    1. ProcessRequestInit()

    //MvcHandler
    private
    void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory) { HttpContext current = HttpContext.Current; if ((current != null) && (ValidationUtility.IsValidationEnabled(current) == true)) { ValidationUtility.EnableDynamicValidation(current); }
       //添加 Http Header : HTTP/1.1 200 OK ......
    this.AddVersionHeader(httpContext); this.RemoveOptionalRoutingParameters();
       //从路由中获取控制器的名称
    string requiredString = this.RequestContext.RouteData.GetRequiredString("controller");
       //获取控制器工厂 factory
    = this.ControllerBuilder.GetControllerFactory();
       //利用控制器工厂创建控制器类 controller
    = factory.CreateController(this.RequestContext, requiredString); if (controller == null) { throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
          MvcResources.ControllerBuilder_FactoryReturnedNull, new object[] { factory.GetType(), requiredString })); } }

    1.1 从路由中获取控制器名称 - GetRequiredString

    public string GetRequiredString(string valueName)
    {
        object obj2;
        if (this.Values.TryGetValue(valueName, out obj2))
        {
            string str = obj2 as string;
            if (!string.IsNullOrEmpty(str))
            {
                return str;
            }
        }
        throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, 
        SR.GetString("RouteData_RequiredValue"), new object[] { valueName })); }

    1.2 获取创建工厂 - GetControllerFactory

    public IControllerFactory GetControllerFactory()
    {
        return this._serviceResolver.Current;
    }

    1.3 创建控制器类 - CreateController

    //DefaultControllerFactory类
    public
    virtual IController CreateController(RequestContext requestContext, string controllerName) { if (requestContext == null) { throw new ArgumentNullException("requestContext"); } if (string.IsNullOrEmpty(controllerName)) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName"); } Type controllerType = this.GetControllerType(requestContext, controllerName); return this.GetControllerInstance(requestContext, controllerType); }

    1.3.1 GetControllerType()

    protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName)
    {
        object obj2;
        Type type;
        if (string.IsNullOrEmpty(controllerName))
        {
            throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
        }
        if ((requestContext != null) && requestContext.RouteData.DataTokens.TryGetValue("Namespaces", out obj2))
        {
            IEnumerable<string> source = obj2 as IEnumerable<string>;
            if ((source != null) && source.Any<string>())
            {
                HashSet<string> namespaces = new HashSet<string>(source, StringComparer.OrdinalIgnoreCase);
                type = this.GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, namespaces);
                if (type == null)
                {
                    bool flag = false;
                    if (!flag.Equals(requestContext.RouteData.DataTokens["UseNamespaceFallback"]))
                    {
                        goto Label_0092;
                    }
                }
                return type;
            }
        }
    Label_0092:
        if (this.ControllerBuilder.DefaultNamespaces.Count > 0)
        {
            HashSet<string> set2 = new HashSet<string>(this.ControllerBuilder.DefaultNamespaces, 
          StringComparer.OrdinalIgnoreCase); type
    = this.GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, set2); if (type != null) { return type; } } return this.GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, null); }

    这里会从路由中, 获取控制器所在的命名空间, 有一点需要注意, 尽量不要在不同的namespace里面命名相同的控制器,不管有这个限制还是没有这个限制, 都是能正常创建控制器的(不重名的情况下). 先看这里.

    private Type GetControllerTypeWithinNamespaces(RouteBase route, string controllerName, HashSet<string> namespaces)
    {
       //MVC-ControllerTypeCache.xml文件中获取Controller的缓存
    this.ControllerTypeCache.EnsureInitialized(this.BuildManager);
       //从之前获取的缓存中, 来获取控制器的类型 ICollection
    <Type> controllerTypes = this.ControllerTypeCache.GetControllerTypes(controllerName, namespaces); switch (controllerTypes.Count) { case 0: return null; case 1: return controllerTypes.First<Type>(); } throw CreateAmbiguousControllerException(route, controllerName, controllerTypes); }

     1.3.2 GetControllerInstance()

    protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (controllerType == null)
        {
            throw new HttpException(0x194, 
          string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_NoControllerFound,   
          new object[] { requestContext.HttpContext.Request.Path })); } if (!typeof(IController).IsAssignableFrom(controllerType)) { throw new ArgumentException(  
          string.Format(CultureInfo.CurrentCulture,
          MvcResources.DefaultControllerFactory_TypeDoesNotSubclassControllerBase,
          new object[] { controllerType }), "controllerType"); } return this.ControllerActivator.Create(requestContext, controllerType); }

    来看一下这里的Create方法, 看一下具体是怎么创建的.

    public IController Create(RequestContext requestContext, Type controllerType)
    {
        IController controller;
        try
        {
            controller = (IController) (this._resolverThunk().GetService(controllerType) ?? 
          Activator.CreateInstance(controllerType)); }
    catch (Exception exception) { throw new InvalidOperationException(
          string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_ErrorCreatingController,
          new object[] { controllerType }), exception); } return controller; }

    这里有一个扩展点, 像之前篇幅提到的Autofac Mvc部分, 在这里, 反馈的GetService(controllerType)就不是一个空值了.

    如果没有搜索到这些扩展, 就会默认反射的方式来创建控制器.

    Demo: 值的注意的是, 这些扩展都是要实现 IDependencyResolve 接口的, 此例子是从Autofac.Integration.Mvc.dll中来的

    public class AutofacDependencyResolver : IDependencyResolver
    {
        // Fields
        private readonly Action<ContainerBuilder> _configurationAction;
        private readonly ILifetimeScope _container;
        private ILifetimeScopeProvider _lifetimeScopeProvider;
    
        // Methods
        public AutofacDependencyResolver(ILifetimeScope container);
        public AutofacDependencyResolver(ILifetimeScope container, ILifetimeScopeProvider lifetimeScopeProvider);
        public AutofacDependencyResolver(ILifetimeScope container, Action<ContainerBuilder> configurationAction);
        public AutofacDependencyResolver(ILifetimeScope container, 
        ILifetimeScopeProvider lifetimeScopeProvider,
        Action<ContainerBuilder> configurationAction); public object GetService(Type serviceType); public IEnumerable<object> GetServices(Type serviceType); // Properties public ILifetimeScope ApplicationContainer { get; } public static AutofacDependencyResolver Current { get; } public ILifetimeScope RequestLifetimeScope { get; } }

    2. Execute()

    这里执行的是ControllerBase的Execute方法. 是一个虚方法. 这里的方法, 留到下一篇分析了.

    protected virtual void Execute(RequestContext requestContext)
    {
        if (requestContext == null)
        {
            throw new ArgumentNullException("requestContext");
        }
        if (requestContext.HttpContext == null)
        {
            throw new ArgumentException(MvcResources.ControllerBase_CannotExecuteWithNullHttpContext, "requestContext");
        }
        this.VerifyExecuteCalledOnce();
       //在这里创建了控制器上下文, ControllerContext
    this.Initialize(requestContext); using (ScopeStorage.CreateTransientScope()) {
         //加载 TempData, 创建及执行 Action, 处理 Action 返回的 ActionResult, 保存TempData数据
    this.ExecuteCore(); } }

    3. ReleaseController()

    这个方法, 顾名思义, 是释放资源的. 来看一下, DefaultControllerFactory 类中的此方法

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

     从上面的分析, 基本可以看到控制器的创建过程, 至于他的执行过程, 就放到下一篇去了, 内容还是很多的.

    二、扩展

    从上面的分析能看到这里有一个依赖注入扩展, 那么下面, 就这个依赖注入扩展, 来举一个小例子.(残破的例子, 别介意, 能演示功能的)

    用的Autofac, 不过与上面不同, 并没有引用上面的程序集, 只引用Autofac.dll一个程序集就可以了.

    我先从之前Autofac篇章, 弄了一个Helper类过来, 精简一下.

    public class IocContainer
    {
        private static ContainerBuilder builder;
    
        private static IContainer container;
    
        static IocContainer()
        {
            builder = new ContainerBuilder();
        }
    
        public static void RegisterTypes(params Type[] types)
        {
            builder.RegisterTypes(types);
        }
    
        public static void Build()
        {
            container = builder.Build();
        }
    
        public static object Create(Type t)
        {
            return container.Resolve(t);
        }
    }

    然后, 我建了一个Resolver.

    public class MyDependencyResolver : IDependencyResolver
    {
        public object GetService(Type serviceType)
        {
            bool flag = true;
            string path = @"F:MVC解析MyControllerFac1.txt";
            if (!File.Exists(path))
            {
                File.Create(path).Close();
            }
    
            try
            {
                return IocContainer.Create(serviceType);
            }
            catch (Exception)
            {
                flag = false;
                //这里需要注意, 需要返回一个null值, 否则会报错
                return null;
            }
            finally
            {
                using (FileStream fs = new FileStream(path, FileMode.Append))
                {
                    var msg = flag ? "命中" : "飘过";
                    byte[] bt = System.Text.Encoding.UTF8.GetBytes(serviceType.ToString() + " : " + msg + "
    ");
                    fs.Write(bt, 0, bt.Length);
               fs.Close(); } } }
    public IEnumerable<object> GetServices(Type serviceType) { var res = new List<object>(); try { var obj = IocContainer.Create(serviceType); res.Add(obj); } catch (Exception) { } return res; } }

    现在前置工作做好了, 可以去MVC中, 注册我自己的方法了.

    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
    
        WebApiConfig.Register(GlobalConfiguration.Configuration);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
       
       //这里我就省懒了, 并没有写到单独的方法中取 IocContainer.RegisterTypes(System.Reflection.Assembly.Load(
    "MyMvc").GetTypes()); IocContainer.Build(); DependencyResolver.SetResolver(new MyDependencyResolver()); }

    OK, 万事俱备, 只差测试. 跑起来吧

    页面能正常访问, 那么是不是走的我哪里呢? 从页面上肯定看不出来, 所以我加了点东西, 看一下1.txt

     从这里来看, 并不是只有 HomeController 走了我自定义的那个方式, 这也是为什么一定要返回一个null了. 对于这些飘过的, 就只能使用MVC默认的方式了. 还是很神奇的.

    目录已同步

  • 相关阅读:
    topcoder srm 681 div1
    topcoder srm 683 div1
    topcoder srm 684 div1
    topcoder srm 715 div1
    topcoder srm 685 div1
    topcoder srm 687 div1
    topcoder srm 688 div1
    topcoder srm 689 div1
    topcoder srm 686 div1
    topcoder srm 690 div1 -3
  • 原文地址:https://www.cnblogs.com/elvinle/p/6289545.html
Copyright © 2011-2022 走看看