ASP.NET WebAPI框架解析第二篇(HttpModule的创建和使用)
我们先看一下执行流程图

图中画红圈的部分便是HttpModule,在说创建HttpModule之前,先说一下HttpApplication对象,HttpApplication对象由Asp.net框架创建,每个请求对应一个HttpApplcation实例对象,Asp.Net框架内部维护了一个HttpApplication对象池,可以复用该对象,以便节省服务器资源。HttpApplication对象内部有许多事件,其中的一些事件如下:
BeginRequest Asp.net处理的第一个事件,表示处理的开始
AuthenticateRequest 验证请求,一般用来取得请求用户的信息
PostResolveRequestCache 已经完成缓存的获取操作
……
EndRequest 本次请求处理完成
其中PostResolveRequestCache 这个事件就被路由模块监听了。我们看看一个标准HttpModule模块的接口定义是怎么样的。
public interface IHttpModule
{
void Init(HttpApplication context);void Dispose();
}
注意到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}");}
然后在VS2017环境下使用IIS Express启动,然后在浏览器输入http://localhost:63210/webapi/home/index,注意在我这里端口号是63210。便可得到如下结果。

我们回顾一下这个流程。
->客户端(在这里是浏览器)发起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实际上是最终处理页面的处理者,后面再说。
ASP.NET WebAPI框架解析第二篇(HttpModule的创建和使用)的更多相关文章
- ASP.NET WebAPI框架解析第一篇
ASP.NET WebAPI有两种寄宿模式,一种是WebHost,一种是SelfHost,为什么可以有两种模式的原因在于WebAPI有一个相对独立的消息处理管道,只要给这个消息管道传递一个封装好的对象 ...
- ASP.NET MVC案例教程(基于ASP.NET MVC beta)——第二篇:第一个页面
摘要 本文首先一步一步完成Demo的第一个页面——首页.然后根据实现过程,说明一下其中用到的与ASP.NET MVC相关的概念与原理. 让第一个页面跑起来 现在,我们来实现公告系统 ...
- ASP.NET Core 学习笔记 第二篇 依赖注入
前言 ASP.NET Core 应用在启动过程中会依赖各种组件提供服务,而这些组件会以接口的形式标准化,这些组件这就是我们所说的服务,ASP.NET Core框架建立在一个底层的依赖注入框架之上,它使 ...
- ASP.NET WebApi 学习与实践系列(1)---如何创建 WebApi
写在前面 最近在做一个app的时候发现需要写后台服务.所以,在考虑是使用webapi还是使用webserver来写这个后台服务的时候.爱纠结的我,最后还是选择了使用webapi来写这个后台服务. 原因 ...
- Lucene&Solr框架之第二篇
2.1.开发环境准备 2.1.1.数据库jar包 我们这里可以尝试着从数据库中采集数据,因此需要连接数据库,我们一直用MySQL,所以这里需要MySQL的jar包 2.1.2.MyBatis的jar包 ...
- SpringMVC框架之第二篇
6.参数绑定(重点) Springmvc作为表现层框架,是连接页面和service层的桥梁,它负责所有请求的中转.怎么从请求中接收参数是重点,这也体现了我们刚开始说的Springmvc的第一个作用:“ ...
- Django框架之第二篇--app注册、静态文件配置、form表单提交、pycharm连接数据库、django使用mysql数据库、表字段的增删改查、表数据的增删改查
本节知识点大致为:静态文件配置.form表单提交数据后端如何获取.request方法.pycharm连接数据库,django使用mysql数据库.表字段的增删改查.表数据的增删改查 一.创建app,创 ...
- Django(三)框架之第二篇
https://www.cnblogs.com/haiyan123/p/7717788.html 一.知识点回顾 1.MTV模型 model:模型,和数据库相关的 template:模板,存放html ...
- Django框架之第二篇
一.知识点回顾 1.MTV模型 model:模型,和数据库相关的 template:模板,存放html文件,模板语法(目的是将变量如何巧妙的嵌入到HTML页面中). views:视图函数 另加urls ...
随机推荐
- JavaScript几种继承方式的总结
1.原型链继承 直接将子类型的原型指向父类型的实例,即"子类型.prototype = new 父类型();",实现方法如下: //父类构造函数 function father(n ...
- 单片机提高ADC精度总结
在常用传感器中,模数转换器是其中至关重要的环节,模数转换器的精度以及系统的成本直接影响到系统的实用性.因此.如何提高模数转换器的精度和降低系统的成本是衡量系统是否具有实际应用价值的标准. 图 1 ...
- C实现进程间通信(管道; 共享内存,信号量)
最近学习了操作系统的并发:以下是关于进程间实现并发,通信的两个方法. 例子: 求100000个浮点数的和.要求: (1)随机生成100000个浮点数(父进程). (2)然后创建4个后代进程,分别求25 ...
- 用TensorFlow搭建一个万能的神经网络框架(持续更新)
我一直觉得TensorFlow的深度神经网络代码非常困难且繁琐,对TensorFlow搭建模型也十分困惑,所以我近期阅读了大量的神经网络代码,终于找到了搭建神经网络的规律,各位要是觉得我的文章对你有帮 ...
- Alpha冲刺 —— 5.1
这个作业属于哪个课程 软件工程 这个作业要求在哪里 团队作业第五次--Alpha冲刺 这个作业的目标 Alpha冲刺 作业正文 正文 github链接 项目地址 其他参考文献 无 一.会议内容 1.展 ...
- 数字电路建模 - jchdl
https://mp.weixin.qq.com/s/uWU6i30_q7wJT3yVJ8yqnQ jchdl:Jianchang Constructed Hardware Description ...
- Java实现 LeetCode 793 阶乘函数后K个零 (分析)
793. 阶乘函数后K个零 f(x) 是 x! 末尾是0的数量.(回想一下 x! = 1 * 2 * 3 * - * x,且0! = 1) 例如, f(3) = 0 ,因为3! = 6的末尾没有0:而 ...
- Java实现 LeetCode 485 最大连续1的个数
485. 最大连续1的个数 给定一个二进制数组, 计算其中最大连续1的个数. 示例 1: 输入: [1,1,0,1,1,1] 输出: 3 解释: 开头的两位和最后的三位都是连续1,所以最大连续1的个数 ...
- Java实现蓝桥杯VIP 算法训练 P0502
试题 算法训练 P0502 资源限制 时间限制:1.0s 内存限制:256.0MB 编写一个程序,读入一组整数,这组整数是按照从小到大的顺序排列的,它们的个数N也是由用户输入的,最多不会超过20.然后 ...
- Java实现 蓝桥杯VIP 算法训练 字符删除
算法训练 字符删除 时间限制:1.0s 内存限制:512.0MB 问题描述 编写一个程序,先输入一个字符串str(长度不超过20),再输入单独的一个字符ch,然后程序会把字符串str当中出现的所有的c ...