码迷,mamicode.com
首页 > Web开发 > 详细

MVC源码解析 - 配置注册 / 动态注册 HttpModule

时间:2017-01-12 09:18:55      阅读:292      评论:0      收藏:0      [点我收藏+]

标签:证明   先来   http   val   ica   来源   action   global   pip   

本来这一篇, 是要继续 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之前的那些事儿

目录已同步

MVC源码解析 - 配置注册 / 动态注册 HttpModule

标签:证明   先来   http   val   ica   来源   action   global   pip   

原文地址:http://www.cnblogs.com/elvinle/p/6269259.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!