• MVC源码解析


    本来这一篇, 是要继续 Pipeline 的, 但是在 Pipeline之前, 我看到了InitModules()方法, 所以决定, 在中间穿插一篇进来. 这一篇来讲一下 IHttpModule 的加载时机, 以及怎么动态注册 HttpModules. 

    一. 经典模式下的 InitModules 方法

    首先来看一下 InitModules() 方法, 在这个方法中, 初始化了所有的module, 其中包括了配置文件中的和想要动态注册的.

    接下来, 看一下方法:

    private void InitModules()
    {
       //注册配置文件中的 HttpModuleCollection modules
    = RuntimeConfig.GetAppConfig().HttpModules.CreateModules();
       //注册想要动态注册的 HttpModuleCollection other
    = this.CreateDynamicModules(); modules.AppendCollection(other); this._moduleCollection = modules; this.InitModulesCommon(); }

    1. 从配置文件中读取

    RuntimeConfig.GetAppConfig().HttpModules.CreateModules()方法, 就是去获取并解析配置文件, 提取出其中的 HttpModule, 并将它加入到集合中.

    internal HttpModuleCollection CreateModules()
    {
        HttpModuleCollection modules = new HttpModuleCollection();
        foreach (HttpModuleAction action in this.Modules)
        {
            modules.AddModule(action.Entry.ModuleName, action.Entry.Create());
        }
        modules.AddModule("DefaultAuthentication", DefaultAuthenticationModule.CreateDefaultAuthenticationModuleWithAssert());
        return modules;
    }

      

    2. 动态注册

    先看一下动态注册的方法吧.

    private HttpModuleCollection CreateDynamicModules()
    {
        HttpModuleCollection modules = new HttpModuleCollection();
        foreach (DynamicModuleRegistryEntry entry in _dynamicModuleRegistry.LockAndFetchList())
        {
            HttpModuleAction action = new HttpModuleAction(entry.Name, entry.Type);
            modules.AddModule(action.Entry.ModuleName, action.Entry.Create());
        }
        return modules;
    }

    注意到这个方法,与上个方法唯一不同的地方就是 遍历的来源不同. OK, 那在看一下那个方法里面干了些什么.

    public ICollection<DynamicModuleRegistryEntry> LockAndFetchList()
    {
        lock (this._lockObj)
        {
            this._entriesReadonly = true;
            return this._entries;
        }
    }

    也就是说, 动态注册的HttpModule必须要加入到 _entries 变量中.

    恰好, HttpApplication 中, 有一个方法, RegisterModule 可以注册module. 来看一下这个方法.

    public static void RegisterModule(Type moduleType)
    {
        if (!RuntimeConfig.GetAppConfig().HttpRuntime.AllowDynamicModuleRegistration)
        {
            throw new InvalidOperationException(SR.GetString("DynamicModuleRegistrationOff"));
        }
        RegisterModuleInternal(moduleType);
    }

    继续看.

    internal static void RegisterModuleInternal(Type moduleType)
    {
        _dynamicModuleRegistry.Add(moduleType);
    }

    这里的 _dynamicModuleRegistry 变量是HttpApplication 中的一个私有字段.

    private static readonly DynamicModuleRegistry _dynamicModuleRegistry;

    继续看这个Add方法.

    public void Add(Type moduleType)
    {
        if (moduleType == null)
        {
            throw new ArgumentNullException("moduleType");
        }
        if (!typeof(IHttpModule).IsAssignableFrom(moduleType))
        {
            throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, 
            SR.GetString("DynamicModuleRegistry_TypeIsNotIHttpModule"),
            new object[] { moduleType }), "moduleType"); } lock (this._lockObj) { if (this._entriesReadonly) { throw new InvalidOperationException(SR.GetString("DynamicModuleRegistry_ModulesAlreadyInitialized")); } this._entries.Add(new DynamicModuleRegistryEntry(MakeUniqueModuleName(moduleType), moduleType.AssemblyQualifiedName)); } }

    看到上面这两句标红的没有, 是不是好像在哪里见过? 其实就是上面的 LockAndFetchList() 方法中的. 但是有一个问题, 在这个方法中,  将变量 _entriesReadonly 标记为 true 了, 也就是说, 如果我们想要动态注册上 HttpModule, 那必须是在这个方法执行之前注册进去才行, 否则是会报错的.

    插叙:

    在集成模式之前, 先插两个例子吧.

    1. Web.config实现方式

    我在类库中, 建了这么一个类:

    public class MyModules : IHttpModule
    {
        public void Dispose() { }
    
        public void Init(HttpApplication context)
        {
            context.BeginRequest += new EventHandler(MyBeginRequest);
        }
    
        void MyBeginRequest(object sender, EventArgs e)
        {
            HttpApplication app = sender as HttpApplication;
            if (app != null)
            {
                app.Response.Write("<p>经过MyModules处理过了</p>");
            }
        }
    }

    然后在<system.webServer>节点下面, 添加一个注册节点.

    <modules>
      <add name="MyModules" type="MyModule.MyModules"/>
    </modules>

    然后, 把程序运行起来, 看一下是否会报错.

     

    实践证明, 是可以的.

    2. 动态注册

    一般如果我想注册一个模块进项目, 首先想到的地方, 就是 Golabl.asax文件中 Application_Start 方法了, 可是写在这个方法里面, 显然并不能满足要求. 

    沿用上面的例子, 然后在 Application_Start()中去注册它, 看一下是否能注册成功.

    那如果我把它写到静态构造函数中去呢, 在这个类第一次加载的时候, 就注册一下试试.

    static MvcApplication()
    {
        HttpApplication.RegisterModule(typeof(MyModules));
    }

    果然是可以的哦.

    但是这里有一个非常大的问题, 让我想不通. 从之前的代码中, 其实可以看到, Application_Start方法的调用其实是非常早的, 但是为什么不能再 Application_Start方法里面注册HttpModules, 而要做到静态构造函数里面. 好吧, 这个问题困扰了我一天, 才发现自己走了岔路. 先不表, 接着看集成模式的吧. 答案就在下面.

    二、集成模式下  InitIntegratedModules 方法

    private void InitIntegratedModules()
    {
        this._moduleCollection = this.BuildIntegratedModuleCollection(_moduleConfigInfo);
        this.InitModulesCommon();
    }

    1. 先看 BuildIntegratedModuleCollection 方法

    private HttpModuleCollection BuildIntegratedModuleCollection(List<ModuleConfigurationInfo> moduleList)
    {
        HttpModuleCollection modules = new HttpModuleCollection();
        foreach (ModuleConfigurationInfo info in moduleList)
        {
            ModulesEntry entry = new ModulesEntry(info.Name, info.Type, "type", null);
            modules.AddModule(entry.ModuleName, entry.Create());
        }
        return modules;
    }

    这里其实就是处理 HttpApplication._moduleConfigInfo 中存放的Module. 那么就产生了一个疑问, 这个HttpApplication._moduleConfigInfo在这里是直接拿出来用的, 但是好像没有任何地方对他进行赋值, 起码, 在我解析的过程中, 并没有看到在哪里对他进行了赋值. 肿么办呢? 

    我立马想到了, 在前面调用过 HttpApplicationFactory.GetSpecialApplicationInstance()方法来产生过一个特殊的HttpApplication对象, 是不是在那里面做了赋值操作呢? 来看一下这个方法.

    private HttpApplication GetSpecialApplicationInstance(IntPtr appContext, HttpContext context)
    {
        HttpApplication application = null;
        lock (this._specialFreeList)
        {
            if (this._numFreeSpecialAppInstances > 0)
            {
                application = (HttpApplication) this._specialFreeList.Pop();
                this._numFreeSpecialAppInstances--;
            }
        }
        if (application == null)
        {
            using (new DisposableHttpContextWrapper(context))
            {
                application = (HttpApplication) HttpRuntime.CreateNonPublicInstance(this._theApplicationType);
                using (new ApplicationImpersonationContext())
                {
                    application.InitSpecial(this._state, this._eventHandlerMethods, appContext, context);
                }
            }
        }
        return application;
    }

    经典模式下看的是 InitInternal 方法, 这里就来看一下 InitSpecial 方法吧.

    internal void InitSpecial(HttpApplicationState state, MethodInfo[] handlers, IntPtr appContext, HttpContext context)
    {
        this._state = state;
        try
        {
            if (context != null)
            {
                this._initContext = context;
                this._initContext.ApplicationInstance = this;
            }
            if (appContext != IntPtr.Zero)
            {
                using (new ApplicationImpersonationContext())
                {
                    HttpRuntime.CheckApplicationEnabled();
                }
                this.InitAppLevelCulture();
                this.RegisterEventSubscriptionsWithIIS(appContext, context, handlers);
            }
            else
            {
                this.InitAppLevelCulture();
                if (handlers != null)
                {
                    this.HookupEventHandlersForApplicationAndModules(handlers);
                }
            }
            if ((appContext != IntPtr.Zero) && ((this._appPostNotifications != 0) || (this._appRequestNotifications != 0)))
            {
                this.RegisterIntegratedEvent(appContext, "global.asax", 
              this._appRequestNotifications,
              this._appPostNotifications,
              base.GetType().FullName, "managedHandler", false);
            }
        }
        finally
        {
            _initSpecialCompleted = true;
            if (this._initContext != null)
            {
                this._initContext.ApplicationInstance = null;
                this._initContext = null;
            }
        }
    }

    这里似乎并不能看出什么, 那么接着来看标红的方法.

    private void RegisterEventSubscriptionsWithIIS(IntPtr appContext, HttpContext context, MethodInfo[] handlers)
    {
        RequestNotification notification;
        RequestNotification notification2;
    this.RegisterIntegratedEvent(appContext, "AspNetFilterModule",
        RequestNotification.LogRequest | RequestNotification.UpdateRequestCache,
        0, string.Empty, string.Empty, true);
    this._moduleCollection = this.GetModuleCollection(appContext); if (handlers != null) { this.HookupEventHandlersForApplicationAndModules(handlers); } HttpApplicationFactory.EnsureAppStartCalledForIntegratedMode(context, this); this._currentModuleCollectionKey = "global.asax"; try { this._hideRequestResponse = true; context.HideRequestResponse = true; this._context = context; this.Init(); } catch (Exception exception) { this.RecordError(exception); Exception error = context.Error; if (error != null) { throw error; } } finally { this._context = null; context.HideRequestResponse = false; this._hideRequestResponse = false; } this.ProcessEventSubscriptions(out notification, out notification2); this._appRequestNotifications |= notification; this._appPostNotifications |= notification2; for (int i = 0; i < this._moduleCollection.Count; i++) { this._currentModuleCollectionKey = this._moduleCollection.GetKey(i); IHttpModule module = this._moduleCollection.Get(i); ModuleConfigurationInfo info = _moduleConfigInfo[i]; module.Init(this); this.ProcessEventSubscriptions(out notification, out notification2); if ((notification != 0) || (notification2 != 0)) { this.RegisterIntegratedEvent(appContext, info.Name, notification, notification2, info.Type, info.Precondition, false); } } this.RegisterIntegratedEvent(appContext, "ManagedPipelineHandler", RequestNotification.ExecuteRequestHandler | RequestNotification.MapRequestHandler, RequestNotification.EndRequest, string.Empty, string.Empty, false); }

    这里有个方法叫 HttpApplication.GetModuleCollection(), 来看一下

    private HttpModuleCollection GetModuleCollection(IntPtr appContext)
    {
        if (_moduleConfigInfo == null)
        {
            List<ModuleConfigurationInfo> list = null;
            IntPtr zero = IntPtr.Zero;
            IntPtr bstrModuleName = IntPtr.Zero;
            int cchModuleName = 0;
            IntPtr bstrModuleType = IntPtr.Zero;
            int cchModuleType = 0;
            IntPtr bstrModulePrecondition = IntPtr.Zero;
            int cchModulePrecondition = 0;
            try
            {
                int count = 0;
                int num5 = UnsafeIISMethods.MgdGetModuleCollection(IntPtr.Zero, appContext, out zero, out count);
                if (num5 < 0)
                {
                    throw new HttpException(SR.GetString("Cant_Read_Native_Modules", new object[] { num5.ToString("X8", CultureInfo.InvariantCulture) }));
                }
                list = new List<ModuleConfigurationInfo>(count);
                for (uint i = 0; i < count; i++)
                {
                    num5 = UnsafeIISMethods.MgdGetNextModule(zero, ref i, out bstrModuleName, out cchModuleName, out bstrModuleType, out cchModuleType, out bstrModulePrecondition, out cchModulePrecondition);
                    if (num5 < 0)
                    {
                        throw new HttpException(SR.GetString("Cant_Read_Native_Modules", new object[] { num5.ToString("X8", CultureInfo.InvariantCulture) }));
                    }
                    string str = (cchModuleName > 0) ? StringUtil.StringFromWCharPtr(bstrModuleName, cchModuleName) : null;
                    string str2 = (cchModuleType > 0) ? StringUtil.StringFromWCharPtr(bstrModuleType, cchModuleType) : null;
                    string condition = (cchModulePrecondition > 0) ? StringUtil.StringFromWCharPtr(bstrModulePrecondition, cchModulePrecondition) : string.Empty;
                    Marshal.FreeBSTR(bstrModuleName);
                    bstrModuleName = IntPtr.Zero;
                    cchModuleName = 0;
                    Marshal.FreeBSTR(bstrModuleType);
                    bstrModuleType = IntPtr.Zero;
                    cchModuleType = 0;
                    Marshal.FreeBSTR(bstrModulePrecondition);
                    bstrModulePrecondition = IntPtr.Zero;
                    cchModulePrecondition = 0;
                    if (!string.IsNullOrEmpty(str) && !string.IsNullOrEmpty(str2))
                    {
                        list.Add(new ModuleConfigurationInfo(str, str2, condition));
                    }
                }
            }
            finally
            {
                if (zero != IntPtr.Zero)
                {
                    Marshal.Release(zero);
                    zero = IntPtr.Zero;
                }
                if (bstrModuleName != IntPtr.Zero)
                {
                    Marshal.FreeBSTR(bstrModuleName);
                    bstrModuleName = IntPtr.Zero;
                }
                if (bstrModuleType != IntPtr.Zero)
                {
                    Marshal.FreeBSTR(bstrModuleType);
                    bstrModuleType = IntPtr.Zero;
                }
                if (bstrModulePrecondition != IntPtr.Zero)
                {
                    Marshal.FreeBSTR(bstrModulePrecondition);
                    bstrModulePrecondition = IntPtr.Zero;
                }
            }
            list.AddRange(this.GetConfigInfoForDynamicModules());
            _moduleConfigInfo = list;
        }
        return this.BuildIntegratedModuleCollection(_moduleConfigInfo);
    }

     好长啊, 很多都看不懂, 不过没关系, 直接看我标红的方法.

    private IEnumerable<ModuleConfigurationInfo> GetConfigInfoForDynamicModules()
    {
        return (from entry in _dynamicModuleRegistry.LockAndFetchList() 

        select new ModuleConfigurationInfo(entry.Name, entry.Type, "managedHandler")); }

    这里也出现了 LockAndFetchList() 方法, 也就是说, 在产生特殊HttpApplication对象的时候, 集成模式下, 在创建特殊对象的过程中, 就已经注册了 HttpModules了, 然后才调用的 Application_Start方法, 这也就解释了, 之前我想不通的问题. 

    其实这里, 主要是我在之前忽略掉了一个问题, IIS的集成模式和经典模式的运行差异性问题. 他们实现的功能其实差不多, 就是过程稍有不同. 

      

    2. InitModulesCommon 方法

    private void InitModulesCommon()
    {
        int count = this._moduleCollection.Count;
        for (int i = 0; i < count; i++)
        {
            this._currentModuleCollectionKey = this._moduleCollection.GetKey(i);
            this._moduleCollection[i].Init(this);
        }
        this._currentModuleCollectionKey = null;
        this.InitAppLevelCulture();
    }

    这里就是循环遍历集合中的HttpModules, 调用他的 Init 方法.

     参考:

      你必须知道的Asp.net知识

      MVC之前的那些事儿

    目录已同步

  • 相关阅读:
    转:win2000/2003 Discuz生存环境搭建及基础优化
    http://db.grussell.org 测试答案 1
    转:linux系统的主机做代理服务器
    C# code 0002
    Welcome to .NET BY C#
    几篇关于Visual Studio Team Foundation Server (TFS) 安装的文章
    VS2003不知道怎么不能编译C文件
    Kaspersky 更新修复
    入行性能测试两个月
    WMI 注册表 StdRegProv
  • 原文地址:https://www.cnblogs.com/elvinle/p/6269259.html
Copyright © 2020-2023  润新知