动态注册HttpModule

2014-06-05 08:58 by 汤姆大叔, 757 阅读, 4 评论, 收藏编辑

文章内容

通过前面的章节,我们知道HttpApplication在初始化的时候会初始化所有配置文件里注册的HttpModules,那么有一个疑问,能否初始化之前动态加载HttpModule,而不是只从Web.config里读取?

答案是肯定的, ASP.NET MVC3发布的时候提供了一个Microsoft.Web.Infrastructure.dll文件,这个文件就是提供了动态注册HttpModule的功能,那么它是如何以注册的呢?我们先去MVC3的源码看看该DLL的源代码。

注:该DLL位置在C:\Program Files\Microsoft ASP.NET\ASP.NET Web Pages\v1.0\Assemblies\下

我们发现了一个静态类DynamicModuleUtility,里面有个RegisterModule方法引起了我的注意:

// Call from PreAppStart to dynamically register an IHttpModule, just as if you had added it to the
// <modules> section in Web.config.
[SecuritySafeCritical]
public static void RegisterModule(Type moduleType) {
if (DynamicModuleReflectionUtil.Fx45RegisterModuleDelegate != null) {
// The Fx45 helper exists, so just call it directly.
DynamicModuleReflectionUtil.Fx45RegisterModuleDelegate(moduleType);
}
else {
// Use private reflection to perform the hookup.
LegacyModuleRegistrar.RegisterModule(moduleType);
}
}

通过代码和注释我们可以看到,这个方法就是让我们动态注册IHttpModule的,而且由于.Net4.5已经有helper类支持了,所以直接就可以用,其它版本使用LegacyModuleRegistrar.RegisterModule来动态注册IHttpModule 的。而这个方法里又分IIS6和IIS7集成或经典模式之分,代码大体上是一致的,我们这里就只分析IIS6版本的代码:

private static void AddModuleToClassicPipeline(Type moduleType) {
// This works by essentially adding a new entry to the <httpModules> section defined
// in ~/Web.config. Need to set the collection to read+write while we do this. // httpModulesSection = RuntimeConfig.GetAppConfig().HttpModules;
// httpModulesSection.Modules.bReadOnly = false;
// httpModulesSection.Modules.Add(new HttpModuleAction(...));
// httpModulesSection.Modules.bReadOnly = true; HttpModulesSection httpModulesSection = null;
try {
object appConfig = _reflectionUtil.GetAppConfig();
httpModulesSection = _reflectionUtil.GetHttpModulesFromAppConfig(appConfig);
_reflectionUtil.SetConfigurationElementCollectionReadOnlyBit(httpModulesSection.Modules, false /* value */); DynamicModuleRegistryEntry newEntry = CreateDynamicModuleRegistryEntry(moduleType);
httpModulesSection.Modules.Add(new HttpModuleAction(newEntry.Name, newEntry.Type));
}
finally {
if (httpModulesSection != null) {
_reflectionUtil.SetConfigurationElementCollectionReadOnlyBit(httpModulesSection.Modules, true /* value */);
}
}
}

上面代码的注释非常重要,通过注释我们可以看到,该方法先从RuntimeConfig.GetAppConfig().HttpModules获取HttpModules集合,然后在集合里添加需要注册的新HttpModule,那就是说HttpApplication在初始化所有HttpModule之前必须将需要注册的HttpModule添加到这个集合里,那是在哪个周期呢?HttpApplication之前是HostingEnvironment,那是不是在这里可以注册呢?我们去该类查看一下相关的代码,在Initialize方法里突然发现一个貌似很熟悉的代码BuildManager.CallPreStartInitMethods(),代码如下:

// call AppInitialize, unless the flag says not to do it (e.g. CBM scenario).
// Also, don't call it if HostingInit failed (VSWhidbey 210495)
if(!HttpRuntime.HostingInitFailed) {
try {
BuildManager.CallPreStartInitMethods();
if ((hostingFlags & HostingEnvironmentFlags.DontCallAppInitialize) == 0) {
BuildManager.CallAppInitializeMethod();
}
}
catch (Exception e) {
// could throw compilation errors in 'code' - report them with first http request
HttpRuntime.InitializationException = e; if ((hostingFlags & HostingEnvironmentFlags.ThrowHostingInitErrors) != 0) {
throw;
}
}
}

通过去BuildManager类去查看该方法的详情,最终发现了如下这个方法:

internal static ICollection<MethodInfo> GetPreStartInitMethodsFromAssemblyCollection(IEnumerable<Assembly> assemblies) {
List<MethodInfo> methods = new List<MethodInfo>();
foreach (Assembly assembly in assemblies) {
PreApplicationStartMethodAttribute[] attributes = null;
try {
attributes = (PreApplicationStartMethodAttribute[])assembly.GetCustomAttributes(typeof(PreApplicationStartMethodAttribute), inherit: true);
}
catch {
// GetCustomAttributes invokes the constructors of the attributes, so it is possible that they might throw unexpected exceptions.
// (Dev10 bug 831981)
} if (attributes != null && attributes.Length != 0) {
Debug.Assert(attributes.Length == 1);
PreApplicationStartMethodAttribute attribute = attributes[0];
Debug.Assert(attribute != null); MethodInfo method = null;
// Ensure the Type on the attribute is in the same assembly as the attribute itself
if (attribute.Type != null && !String.IsNullOrEmpty(attribute.MethodName) && attribute.Type.Assembly == assembly) {
method = FindPreStartInitMethod(attribute.Type, attribute.MethodName);
} if (method != null) {
methods.Add(method);
}
else {
throw new HttpException(SR.GetString(SR.Invalid_PreApplicationStartMethodAttribute_value,
assembly.FullName,
(attribute.Type != null ? attribute.Type.FullName : String.Empty),
attribute.MethodName));
}
}
}
return methods;
}

发现了该方法会查找应用程序下所有的程序集,判断如果程序集标记为PreApplicationStartMethodAttribute特性,就会执行这个特性里指定的方法(静态方法),再检查该类的代码:

    [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
public sealed class PreApplicationStartMethodAttribute : Attribute {
private readonly Type _type;
private readonly string _methodName;
public PreApplicationStartMethodAttribute(Type type, string methodName) {
_type = type;
_methodName = methodName;
} public Type Type { get { return _type; } }
public string MethodName { get { return _methodName; } }
}

这时候,心里应该有数了吧,我们可以在这里指定一个静态方法名称,然后在该方法去通过如下代码去注册一个自定义的HttpModule(注意我们只能使用一次):

DynamicModuleUtility.RegisterModule(typeof(CustomModule));

我们来做个试验试试我们分析的结果是不是正确,首先创建一个自定义HttpModule,代码如下:

public class CustomModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(context_BeginRequest); } void context_BeginRequest(object sender, EventArgs e)
{
HttpApplication ap = sender as HttpApplication;
if (ap != null)
{
ap.Response.Write("汤姆大叔测试PreApplicationStartMethod通过!<br/>");
}
} public void Dispose()
{
//nothing to do here
}
}

然后在创建一个用于注册这个HttpModule的类并且带有静态方法:

public class PreApplicationStartCode
{
private static bool hasLoaded; public static void PreStart()
{
if (!hasLoaded)
{
hasLoaded = true;
//注意这里的动态注册,此静态方法在Microsoft.Web.Infrastructure.DynamicModuleHelper
DynamicModuleUtility.RegisterModule(typeof(CustomModule));
}
}
}

接着要安装要求对该程序添加一个特性,代码如下:

[assembly: PreApplicationStartMethod(typeof(WebApplication1.Test.PreApplicationStartCode), "PreStart")]

最后,编译运行,会发现所有的页面顶部都会出现我们指定的文字(汤姆大叔测试PreApplicationStartMethod通过!),截图如下:

这就证实了我们的分析结果是正确的,怎么样,这个功能不错吧,不需要每次都在web.config里定义你的HttpModule咯,而且我们以后封装自己的类库就方便多了,不需要在网站程序集里指定代码去启动自己封装好的单独类库了,因为我们可以在自己的类库里直接使用这种方式实现自动注册的功能。下个章节,我们将介绍一个利用此功能开发的超强类库。

注:同一个程序集只能使用一次这个特性来调用一个静态方法。

同步与推荐

本文已同步至目录索引:MVC之前的那点事儿系列

MVC之前的那点事儿系列文章,包括了原创,翻译,转载等各类型的文章,如果对你有用,请推荐支持一把,给大叔写作的动力。

 
 

动态注册HttpModule的更多相关文章

  1. 你必须知道ASP.NET知识------关于动态注册httpmodule(对不起汤姆大叔)

    一.关于动态注册的问题 很多人看过汤姆大叔的MVC之前的那点事儿系列(6):动态注册HttpModule ,其实汤姆大叔没有发现httpmodule动态注册的根本机制在哪里. 亦即:怎么动态注册?为什 ...

  2. MVC之前的那点事儿系列(6):动态注册HttpModule

    文章内容 通过前面的章节,我们知道HttpApplication在初始化的时候会初始化所有配置文件里注册的HttpModules,那么有一个疑问,能否初始化之前动态加载HttpModule,而不是只从 ...

  3. 不使用配置文件动态注册HttpModule

    在asp.net 4.0中,提供了一种不通过修改配置文件注册Module的方法.从.net3.5开始,新提供的PreApplicationStartMethodAttribute特性可以应用在程序集上 ...

  4. Mvc动态注册HttpModule详解

    序言 注册Httpmodule可以让我们使用HttpApplication对象中的处理管道事件.目前大家所熟知的应该有2种方式来使用HttpApplication对象中的处理管道事件.第一种是通过Gl ...

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

    本来这一篇, 是要继续 Pipeline 的, 但是在 Pipeline之前, 我看到了InitModules()方法, 所以决定, 在中间穿插一篇进来. 这一篇来讲一下 IHttpModule 的加 ...

  6. 在Asp.net 4.0 中动态注册HttpModule

    using System; using System.Web; using Microsoft.Web.Infrastructure; namespace MvcApplication1 { publ ...

  7. 动态注册HttpModule管道,实现global.asax功能

    1.所用类库有 Microsoft.Web.Infrastructure.dll 和WebActivator.dll 2.类代码如下 using System; using System.Collec ...

  8. RPC原来就是Socket——RPC框架到dubbo的服务动态注册,服务路由,负载均衡演化

    序:RPC就是使用socket告诉服务端我要调你的哪一个类的哪一个方法然后获得处理的结果.服务注册和路由就是借助第三方存储介质存储服务信息让服务消费者调用.然我们自己动手从0开始写一个rpc功能以及实 ...

  9. Android开发4: Notification编程基础、Broadcast的使用及其静态注册、动态注册方式

    前言 啦啦啦~(博主每次开篇都要卖个萌,大家是不是都厌倦了呢~) 本篇博文希望帮助大家掌握 Broadcast 编程基础,实现动态注册 Broadcast 和静态注册 Broadcast 的方式以及学 ...

随机推荐

  1. Ajax 实现无刷新页面

    注意:如本文所用,在前面的文章库的数目可以在源代码中找到,我将指示在文本,其中链路,为了缩短制品的长度,阅读由此带来的不便.乞求被原谅. 评论文章 Ajax 实现无刷新页面.其原理.代码库.代码. 这 ...

  2. PHP Yii框架开发——组织架构网站重构

    最近一段时间在维护公司的组织架构网站(Org),旧版网站只是用了xampp简单搭建了一套环境部署在了windows机器上,代码结构相对简单. 整个架构如下: 整个架构没有用到复杂的结构,class里放 ...

  3. SharePoint 如何使自己的网页自动跳转

    SharePoint 如何使自己的网页自动跳转         SharePoint自动制作自己的网页跳的很easy,只有在页面上要添加一个Web部分--内容编辑器,对应的js代码就可以.       ...

  4. 允许Android随着屏幕转动的控制自由转移到任何地方(附demo)

    在本文中,Android ViewGroup/View流程,及经常使用的自己定义ViewGroup的方法.在此基础上介绍动态控制View的位置的三种方法,并给出最佳的一种方法. 一.ViewGroup ...

  5. ZOJ 1649:Rescue(BFS)

    Rescue Time Limit: 2 Seconds      Memory Limit: 65536 KB Angel was caught by the MOLIGPY! He was put ...

  6. Eddy's mistakes(字母大小写转换)strlwr函数的应用

    Problem Description Eddy usually writes  articles ,but he likes mixing the English letter uses, for ...

  7. shell 命名管道,进程间通信

    命名管道基础 命名管道也被称为FIFO文件, 在文件系统中是可见的,并且跟其它文件一样可以读写! 命名管道特点: 当写进程向管道中写数据的时候,如果没有进程读取这些数据,写进程会堵塞 当读取管道中的数 ...

  8. Swift中文手册 -- The Basics

    原文:Swift中文手册 -- The Basics 基础部分 Swift 是 iOS 和 OS X 应用开发的一门新语言.然而,如果你有 C 或者 Objective-C 开发经验的话,你会发现 S ...

  9. cocos2dx3.2 推断音效是否播放

    SimpleAudioEngine类中增加一函数 例如以下 bool isEffectPlaying(unsigned int nSoundId); 定义例如以下 bool SimpleAudioEn ...

  10. 数据传输对象(DTO)介绍及各类型实体比较

    数据传输对象(DTO)介绍及各类型实体比较 本文将介绍DDD分层架构中广泛使用的数据传输对象Dto,并且与领域实体Entity,查询实体QueryObject,视图实体ViewModel等几种实体进行 ...