我们先看一下执行流程图

图中画红圈的部分便是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的创建和使用)的更多相关文章

  1. ASP.NET WebAPI框架解析第一篇

    ASP.NET WebAPI有两种寄宿模式,一种是WebHost,一种是SelfHost,为什么可以有两种模式的原因在于WebAPI有一个相对独立的消息处理管道,只要给这个消息管道传递一个封装好的对象 ...

  2. ASP.NET MVC案例教程(基于ASP.NET MVC beta)——第二篇:第一个页面

    摘要      本文首先一步一步完成Demo的第一个页面——首页.然后根据实现过程,说明一下其中用到的与ASP.NET MVC相关的概念与原理. 让第一个页面跑起来      现在,我们来实现公告系统 ...

  3. ASP.NET Core 学习笔记 第二篇 依赖注入

    前言 ASP.NET Core 应用在启动过程中会依赖各种组件提供服务,而这些组件会以接口的形式标准化,这些组件这就是我们所说的服务,ASP.NET Core框架建立在一个底层的依赖注入框架之上,它使 ...

  4. ASP.NET WebApi 学习与实践系列(1)---如何创建 WebApi

    写在前面 最近在做一个app的时候发现需要写后台服务.所以,在考虑是使用webapi还是使用webserver来写这个后台服务的时候.爱纠结的我,最后还是选择了使用webapi来写这个后台服务. 原因 ...

  5. Lucene&Solr框架之第二篇

    2.1.开发环境准备 2.1.1.数据库jar包 我们这里可以尝试着从数据库中采集数据,因此需要连接数据库,我们一直用MySQL,所以这里需要MySQL的jar包 2.1.2.MyBatis的jar包 ...

  6. SpringMVC框架之第二篇

    6.参数绑定(重点) Springmvc作为表现层框架,是连接页面和service层的桥梁,它负责所有请求的中转.怎么从请求中接收参数是重点,这也体现了我们刚开始说的Springmvc的第一个作用:“ ...

  7. Django框架之第二篇--app注册、静态文件配置、form表单提交、pycharm连接数据库、django使用mysql数据库、表字段的增删改查、表数据的增删改查

    本节知识点大致为:静态文件配置.form表单提交数据后端如何获取.request方法.pycharm连接数据库,django使用mysql数据库.表字段的增删改查.表数据的增删改查 一.创建app,创 ...

  8. Django(三)框架之第二篇

    https://www.cnblogs.com/haiyan123/p/7717788.html 一.知识点回顾 1.MTV模型 model:模型,和数据库相关的 template:模板,存放html ...

  9. Django框架之第二篇

    一.知识点回顾 1.MTV模型 model:模型,和数据库相关的 template:模板,存放html文件,模板语法(目的是将变量如何巧妙的嵌入到HTML页面中). views:视图函数 另加urls ...

随机推荐

  1. 解决docker创建的elasticsearch-head容器不能连接elasticsearch等问题

    在使用docker创建elasticsearch-head容器去连接elasticsearch的时候,容易出两个问题 1.不能连接elasticsearch 修改elasticsearch.yml文件 ...

  2. 四、Spring-面向切面编程

    内容 面向切面编程基本原理 通过POJO创建切面 使用@AspectJ注解 为AspectJ切面注入依赖 关键词 横切关注点(cross-cutting concern) 继承 (inheritanc ...

  3. Rocket - tilelink - Broadcast

    https://mp.weixin.qq.com/s/-pjCLzzincJz0Z66orx8kg   介绍Broadcast的实现.   ​​   1. 基本介绍   TLBroadcast实现的是 ...

  4. Java实现 LeetCode 581 最短无序连续子数组(从两遍搜索找两个指针)

    581. 最短无序连续子数组 给定一个整数数组,你需要寻找一个连续的子数组,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序. 你找到的子数组应是最短的,请输出它的长度. 示例 1: 输入: ...

  5. Java实现 LeetCode 416 分割等和子集

    416. 分割等和子集 给定一个只包含正整数的非空数组.是否可以将这个数组分割成两个子集,使得两个子集的元素和相等. 注意: 每个数组中的元素不会超过 100 数组的大小不会超过 200 示例 1: ...

  6. Java实现 LeetCode 129 求根到叶子节点数字之和

    129. 求根到叶子节点数字之和 给定一个二叉树,它的每个结点都存放一个 0-9 的数字,每条从根到叶子节点的路径都代表一个数字. 例如,从根到叶子节点路径 1->2->3 代表数字 12 ...

  7. Java实现 蓝桥杯VIP 算法提高 change

    算法提高 change 时间限制:1.0s 内存限制:256.0MB 问题描述 数组A中共有n个元素,初始全为0.你可以对数组进行两种操作:1.将数组中的一个元素加1:2.将数组中所有元素乘2.求将数 ...

  8. window10下通过docker安装swoole,运行laravel-swoole服务

    最近公司压测一个接口,用laravel框架,业务逻辑就是从缓存中取数据,tps总是上不去,于是决定换下swoole来替换php-fpm,来处理php请求,tps比原来高了好几倍. 现在有个问题就是wi ...

  9. zabbix3.2升级3.4报错Database error

    摘要: zabbix3.2版本升级到zabbix3.4版本后打开页面报错,报错内容如下 Database errorThe frontend does not match Zabbix databas ...

  10. Oracle数据迁移后由列的直方图统计信息引起的执行计划异常

    (一)问题背景 在使用impdp进行数据导入的时候,往往在导入表和索引的统计信息的时候,速度非常慢,因此我在使用impdp进行导入时,会使用exclude=table_statistics排除表的统计 ...