咱们先看一下执行流程图

图中画红圈的部分即是HttpModule,在说建立HttpModule以前,先说一下HttpApplication对象,HttpApplication对象由Asp.net框架建立,每一个请求对应一个HttpApplcation实例对象,Asp.Net框架内部维护了一个HttpApplication对象池,能够复用该对象,以便节省服务器资源。HttpApplication对象内部有许多事件,其中的一些事件以下:
BeginRequest Asp.net处理的第一个事件,表示处理的开始html
AuthenticateRequest 验证请求,通常用来取得请求用户的信息web
PostResolveRequestCache 已经完成缓存的获取操做api
……浏览器
EndRequest 本次请求处理完成缓存
其中PostResolveRequestCache 这个事件就被路由模块监听了。咱们看看一个标准HttpModule模块的接口定义是怎么样的。
public interface IHttpModule
{
void Init(HttpApplication context);服务器
void Dispose();
app
}
注意到Init(HttpApplication context)这个方法,每注册一个HttpModule模块,Asp.Net框架内部经过反射获取对应的程序集,并经过反射调用Init方法,Context参数即是处理一个Http请求的HttpApplication实例对象。那怎么让咱们注册的模块参与处处理Http请求中呢?前面提到HttpApplication内部有许多事件,而咱们的HttpModule中的Init方法会被Asp.Net框架调用,因此咱们能够利用这个去注册相应的事件,执行咱们的代码,咱们在这里能够作不少事,好比自定义验证、自定义受权,图片压缩,图片加水印,服务器日志记录、恶意请求拦截等等。
为了演示自定义HttpModule的使用,咱们建立一个寄宿模式为WebHost的WebAPI,为了便于理解ASP.NET WebAPI的组成,咱们不直接建立一个WebAPI模板的项目。
1.使用VS2017建立一个空的解决方案

2.新建一个空的.NetFramwork类库项目和一个空的ASP.NET Web应用



咱们对WebAPI类库项目引用WebAPI所需的System.Web.Http.dll。我在I:\Program Files (x86)\Microsoft Visual Studio\Shared\Packages\Microsoft.AspNet.WebApi.Core.5.2.7\lib\net45路径下找到了该dll。

而后在WebAPI项目下创建一个类,使用命名空间System.Web.Htpp,继承该命名空间下的ApiController便可,而后定义一个方法,我定义的以下。注意类名必定要以Controller结尾,不然在路由匹配成功后,WebAPI框架内部不能经过路由解析到的值去反射构建对应的控制器实例对象。

而后对WebHost项目引用以下dll(我都是在I:\Program Files (x86)\Microsoft Visual Studio\Shared\Packages\目录下找到的,实在找不到就建立一个WEBAPI模板而后到项目目录下的Package文件中去拷贝)
System.Web.Http.dll
System.Web.Http.WebHost.dll
System.Net.Http.Formatting.dll
WebHost项目还要保持对WebAPI项目的引用。
为WebHost项目添加Global.asax文件,VS为咱们自动建立了一个Global类,并继承于System.Web命名空间下的HttpApplication类。这个类就是处理Http请求的类。咱们也能够在这里监听相应的事件,监听函数格式以XXX_XXX的形式定义,至于为何要这么定义的缘由是为了便于经过反射进行注册。有些事件只能在这里监听,好比Application_Start,Application_Start在整个应用生命周期中只会执行一次,至关于静态构造函数.咱们在Application_Start中去注册全局的路由表,在WebHost模式下,WebAPI的路由系统实际上由ASP.NET的路由系统完成的,固然WebAPI自己的路由系统也能够完成。咱们注册如下路由表。
protected void Application_Start(object sender, EventArgs e)
{
GlobalConfiguration.Configuration.Routes.MapHttpRoute(name: "first",routeTemplate: "webapi/{controller}/{action}");
框架
}

咱们回顾一下这个流程。
->客户端(在这里是浏览器)发起HTTP请求
->IIS Express接收到该请求
->IIS Express发现该请求路径不是已知的静态资源类型,进而把请求交给Asp.Net托管代码处理
->Asp.Net从HttpApplication池中取一个实例对象去处理该Http请求
->注册相应HttpModule模块,并调用Init()函数,这个时候HttpContext尚未造成,咱们只能在这里注册相应的监听事件函数。
->HttpContext造成,HttpApplication实例对象内部的事件轮流触发,其中有一个PostMapRequestHandler事件,在这个事件触发后,会调用相应HttpHandler执行相应的处理,后面再说HttpHandler
->必要的事件触发完成以后生成Http报文并交由IIS Express返回给客户端
->客户端接受Http报文并解析显示在客户端中。
这只是一个大概的过程,具体比这复杂,但咱们关注点在HttpModule上,因此足够了。
咱们如今尝试本身建立HttpModule
我在WebHost项目下创建了一个文件夹,而后新建了一个AuthorizeHttpModule,模拟受权,功能很简单,看Http请求头中是否包含name字段,而且值为HK,不然,向Http响应报文中的Body部分写入内容,并拦截请求。

namespace WebHost.HttpModules
{
public class AuthorizeHttpModule : IHttpModule
{
public void Dispose()
{
return;
}函数
public void Init(HttpApplication context)
{
//此时HttpContext还未构建完成,不能在这里操做HttpContext
//注册事件监听函数
context.BeginRequest += Authorize;
}
private void Authorize(object sender,EventArgs e)
{
HttpApplication app = sender as HttpApplication;编码
if (app.Request.Headers.Get("name") != "HK")
{
//不加这一行客户端可能不能自动正确的解析字符编码
app.Response.Headers.Add("content-Type", "text/html;charset=utf-8");
//经过Write(string s)写入的字符串在内部默认被转换为utf-8编码。C#string默认编码为UTF-16
//要先写入原始的字符串编码,调用BinaryWrite(byte[] bytes)
app.Response.Write("验证不经过!");
app.CompleteRequest();
}
}
}
}
在WebHost项目目录下的Web.Config中配置HttpModule信息,以便Asp.Net框架读取。注意,有个全局的Web.Config文件,其中默认注册了许多HttpModule,其中有一个HttpModule(WebDAVModule)会截断Put和Delete请求。

如上,我添加了system.webServer节点(IIS集成模式下是这样配置,经典模式自行搜索),并在子节点modules下添加了自定义的HttpModule,其中name是HttpModule名称,ASP.NET内部会以这个名称做为Key,type表示HttpModule的类型名,以便反射读取相应的类型对象。该类型所在的程序集必须在Web应用程序的Bin目录,不然不能正常加载,因为我建立的HttpMudle是在WebHost项目中,因此不会出现问题。咱们如今启动IIS Express看看效果。

能够看到咱们的HttpModule起做用了。咱们用PostMan为请求头添加一个字段name,值为HK,而后请求/webapi/home/index试试看

能够看到没有被拦截。前面咱们提到了一个HttpHandler,这个HttpHandler其实是最终处理页面的处理者,后面再说。