我们先看一下执行流程图

图中画红圈的部分便是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. ## 0521Day04内部类

    [重点] Math公式 静态导入 正则表达式 内部类 访问修饰符 [Math] Math包的相关方法: round:四舍五入:-10.9==>-11/-11.2==>-11 floor:向 ...

  2. PIC单片机编译器自带的延时程序

    https://wenku.baidu.com/view/3c94e2934028915f814dc205.html

  3. 搭建Nexus Repository包管理系统

    搭建Nexus Repository包管理系统 下载安装程序 下载Nexus Repository最新版本 配置说明 将下载后的文件传输到服务器上 #修改配置文件 vi /etc/security/l ...

  4. 使用Redis分布式锁实现主备

    使用Redis分布式锁实现集群的主备 最近工作中遇到一个问题,我们会调用业务部门提供的HTTP接口获取所有的音视频任务信息,这些任务会被分发到各个机器节点进行处理.有两个方案: 方案一 为每台机器编号 ...

  5. CVE-2016-3714-ImageMagick 漏洞利用

    漏洞简介:/etc/ImageMagick/delegates.xml 将%s,%l加入到command里造成了命令执行 利用方式: poc代码: push graphic-context viewb ...

  6. Rocket - tilelink - BusWrapper

    https://mp.weixin.qq.com/s/03BvgTNQtD75Guco6gUGQg   简单介绍BusWrapper的实现.   1. HasTLBusParams   定义SoC的挂 ...

  7. jchdl - 门和开关层(GSL)

    https://mp.weixin.qq.com/s/dcBfMLOuaFtrk6i149vIVQ   第一部分 静态建模:拓扑模型   GSL层拓扑建模相对简单,由线和节点组成: 线连接各个节点: ...

  8. Vue中keep-alive的使用

    Vue中keep-alive的使用我总结的有两种方式应用: 首先简述一下keep-alive的作用,kee-alive可以缓存不活动的的组件.当组件之间进行相互切换的时候,默认会销毁,当重新切换回来时 ...

  9. 这才是你需要的最基础的.Net基础面试题(通俗易懂,最基础的.Net)2

    51. 委托回调静态方法和实例方法有何区别? 当一个实例方法被调用时,需要通过实例对象来访问,绑定一个实例方法到委托必须同时让委托得到实例方法的代码段和实例对象的信息,这样在委托被回调时候.NET才能 ...

  10. Java实现 LeetCode 409 最长回文串

    409. 最长回文串 给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串. 在构造过程中,请注意区分大小写.比如 "Aa" 不能当做一个回文字符串. 注意 ...