文章内容

通过前面的章节,我们知道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) == ) {
BuildManager.CallAppInitializeMethod();
}
}
catch (Exception e) {
// could throw compilation errors in 'code' - report them with first http request
HttpRuntime.InitializationException = e; if ((hostingFlags & HostingEnvironmentFlags.ThrowHostingInitErrors) != ) {
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 != ) {
Debug.Assert(attributes.Length == );
PreApplicationStartMethodAttribute attribute = attributes[];
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之前的那点事儿系列文章,包括了原创,翻译,转载等各类型的文章,如果对你有用,请推荐支持一把,给大叔写作的动力。

MVC之前的那点事儿系列(6):动态注册HttpModule的更多相关文章

  1. MVC之前的那点事儿系列(10):MVC为什么不再需要注册通配符(*.*)了?

    文章内容 很多教程里都提到了,在部署MVC程序的时候要配置通配符映射(或者是*.mvc)到aspnet_ISPAI.dll上,在.NET4.0之前确实应该这么多,但是.NET4.0之后已经不要再费事了 ...

  2. MVC之前的那点事儿系列(8):UrlRouting的理解

    文章内容 根据对Http Runtime和Http Pipeline的分析,我们知道一个ASP.NET应用程序可以有多个HttpModuel,但是只能有一个HttpHandler,并且通过这个Http ...

  3. MVC之前的那点事儿系列(9):MVC如何在Pipeline中接管请求的?

    文章内容 上个章节我们讲到了,可以在HttpModules初始化之前动态添加Route的方式来自定义自己的HttpHandler,最终接管请求的,那MVC是这么实现的么?本章节我们就来分析一下相关的M ...

  4. MVC之前的那点事儿系列(7):WebActivator的实现原理详解

    文章内容 上篇文章,我们分析如何动态注册HttpModule的实现,本篇我们来分析一下通过上篇代码原理实现的WebActivator类库,WebActivator提供了3种功能,允许我们分别在Http ...

  5. MVC之前的那点事儿系列(5):Http Pipeline详细分析(下)

    文章内容 接上面的章节,我们这篇要讲解的是Pipeline是执行的各种事件,我们知道,在自定义的HttpModule的Init方法里,我们可以添加自己的事件,比如如下代码: public class ...

  6. MVC之前的那点事儿系列(4):Http Pipeline详细分析(上)

    文章内容 继续上一章节的内容,通过HttpApplicationFactory的GetApplicationInstance静态方法获取实例,然后执行该实例的BeginProcessRequest方法 ...

  7. MVC之前的那点事儿系列(3):HttpRuntime详解分析(下)

    文章内容 话说,经过各种各样复杂的我们不知道的内部处理,非托管代码正式开始调用ISPAIRuntime的ProcessRequest方法了(ISPAIRuntime继承了IISPAIRuntime接口 ...

  8. MVC之前的那点事儿系列(2):HttpRuntime详解分析(上)

    文章内容 从上章文章都知道,asp.net是运行在HttpRuntime里的,但是从CLR如何进入HttpRuntime的,可能大家都不太清晰.本章节就是通过深入分析.Net4的源码来展示其中的重要步 ...

  9. MVC之前的那点事儿系列(1):进入CLR

    MVC之前的那点事儿系列,是笔者在2012年初阅读MVC3源码的时候整理的,主要讲述的是从HTTP请求道进入MVCHandler之前的内容,包括了原创,翻译,转载,整理等各类型文章,当然也参考了博客园 ...

随机推荐

  1. AngularJs学习的前景及优势

    一.趋势 互联网未来的发展趋势是前端后端只靠json数据来进行通信.后端只处理和发送一段json数据到前端,然后计算和模板渲染都在前端进行,而前端的改动,形成json数据然后传回到后端.未来趋势就是: ...

  2. [转] 编译安装GCC

    Linux下编写C/C++程序自然缺不了一个优秀的编译器,Linux下比较常见的自然是GCC了. 2015年GCC也出到了5.2.0版本,对于C++11/14也有了更好的支持了. 所以,今天我们就来说 ...

  3. ASP.NET 5 (vNext) 理解和概述

    概述 ASP.NET 5 (又称为vNext) 是自ASP.NET产生15年以来一次革命性的更新, 我们可以从以下几点来理解其概貌和意义: ASP.NET 5是开源的 ASP.NET 5开发的WebA ...

  4. [Asp.net 开发系列之SignalR篇]专题二:使用SignalR实现酷炫端对端聊天功能

    一.引言 在前一篇文章已经详细介绍了SignalR了,并且简单介绍它在Asp.net MVC 和WPF中的应用.在上篇博文介绍的都是群发消息的实现,然而,对于SignalR是为了实时聊天而生的,自然少 ...

  5. Homework 1 -- The beginning

    我是在北京在读的一位大学生.如果问我学的什么专业,我会用一个冷笑话回答你:我精通多种语言,在老家我说家乡话:跟北京我讲普通话:跟老外就玩English:我跟机器得敲代码.现在你知道我学的就是计算机了. ...

  6. 手把手搭建WAMP+PHP+SVN开发环境

    一:WAMP 这款软件在安装的过程中就已经把Apache.MySQL.PHP继承好了,而且也做好了相应的配置,除此之外,还加上了SQLitemanager和Phpmyadmin,省去了很多复杂的配置过 ...

  7. 纠结于搞.Net待遇不高的同学入...

    最近看到不少抱怨搞.net工资低的帖子.别的方向我不是太清楚,作为搞了近8年.Net信息系统开发的码农也想发表下自己的意见. 由于我的阅历和能力有限,首先想限定下本文的范围.这里说的“信息系统”主要包 ...

  8. 基于Task的异步模式的定义

    返回该系列目录<基于Task的异步模式--全面介绍> 命名,参数和返回类型 在TAP(Task-based Asynchronous Pattern)中的异步操作的启动和完成是通过一个单独 ...

  9. mysql C API的使用

    <MySQL++简介>介绍了如何使用C++来访问mysql,本文记录下使用C API访问mysql,mysql++就是对本文介绍的C-API的封装. 常用函数(名字就能告诉我们用法): M ...

  10. 每天一个linux命令(54):ping命令

    Linux系统的ping命令是常用的网络命令,它通常用来测试与目标主机的连通性,我们经常会说“ping一下某机器,看是不是开着”.不能打开网页时会说“你先ping网关地址192.168.1.1试试”. ...