服务器

位置:IT落伍者 >> 服务器 >> 浏览文章

HttpModule与HttpHandler详解


发布日期:2023年05月24日
 
HttpModule与HttpHandler详解

ASPNET对请求处理的过程

当请求一个*aspx文件的时候这个请求会被inetinfoexe进程截获它判断文件的后缀(aspx)之后将这个请求转交给 ASPNET_ISAPIdllASPNET_ISAPIdll会通过http管道(Http PipeLine)将请求发送给ASPNET_WPexe进程在ASPNET_WPexe进程中通过HttpRuntime来处理这个请求处理完毕将结果返回客户端

inetinfoexe进程是www服务的进程IIS服务和ASPNET_ISAPIDLL都寄存在此进程中

ASPNET_ISAPIDLL是处理aspx文件的win组件其实IIS服务器是只能识l文件的当IIS服务器发现被请求的文件是aspx文件时IIS服务器将其交给aspnet_isapidll来处理

aspnet_wpexe进程ASPNET框架进程提运行的托管环境的CLR(公共语言运行时)就是寄存在此进程中

ASPNET Framework处理一个Http Request的流程

HttpRequest>inetinfoexe>ASPNET_ISAPIdll>ASPNET_WPexe>HttpRuntime>HttpApplication Factory>HttpApplication>HttpModule>HttpHandler Factory>HttpHandler>HttpHandlerProcessRequest()

ASPNET请求处理过程是基于管道模型的这个管道模型是由多个HttpModule和HttpHandler组成ASPNET 把http请求依次传递给管道中各个HttpModule最终被HttpHandler处理处理完成后再次经过管道中的HTTP模块把结果返回给客户端我们可以在每个HttpModule中都可以干预请求的处理过程

注意在http请求的处理过程中只能调用一个HttpHandler但可以调用多个HttpModule

当请求到达HttpModule的时候系统还没有对这个请求真正处理但是我们可以在这个请求传递到处理中心(HttpHandler)之前附加一些其它信息或者截获的这个请求并作一些额外的工作也或者终止请求等在HttpHandler处理完请求之后我们可以再在相应的HttpModule中把请求处理的结果进行再次加工返回客户端

HttpModule

HTTP模块是实现了SystemWebIhttpModule接口的类

IHttpModule接口的声明

public interface IHttpModule

{

void Init (HttpApplication context);

void Dispose ();

}

Init 方法系统初始化的时候自动调用这个方法允许HTTP模块向HttpApplication 对象中的事件注册自己的事件处理程序

Dispose方法 这个方法给予HTTP模块在对象被垃圾收集之前执行清理的机会此方法一般无需编写代码

HTTP模块可以向SystemWebHttpApplication对象注册下面一系列事件

AcquireRequestState 当ASPNET运行时准备好接收当前HTTP请求的对话状态的时候引发这个事件

AuthenticateRequest 当ASPNET 运行时准备验证用户身份的时候引发这个事件

AuthorizeRequest 当ASPNET运行时准备授权用户访问资源的时候引发这个事件

BeginRequest 当ASPNET运行时接收到新的HTTP请求的时候引发这个事件

Disposed 当ASPNET完成HTTP请求的处理过程时引发这个事件

EndRequest 把响应内容发送到客户端之前引发这个事件

Error 在处理HTTP请求的过程中出现未处理异常的时候引发这个事件

PostRequestHandlerExecute 在HTTP处理程序结束执行的时候引发这个事件

PreRequestHandlerExecute 在ASPNET开始执行HTTP请求的处理程序之前引发这个事件在这个事件之后ASPNET 把该请求转发给适当的HTTP处理程序

PreSendRequestContent 在ASPNET把响应内容发送到客户端之前引发这个事件这个事件允许我们在内容到达客户端之前改变响应内容我们可以使用这个事件给页面输出添加用于所有页面的内容例如通用菜单头信息或脚信息

PreSendRequestHeaders 在ASPNET把HTTP响应头信息发送给客户端之前引发这个事件在头信息到达客户端之前这个事件允许我们改变它的内容我们可以使用这个事件在头信息中添加cookie和自定义数据

ReleaseRequestState 当ASPNET结束所搜有的请求处理程序执行的时候引发这个事件

ResolveRequestCache 我们引发这个事件来决定是否可以使用从输出缓沖返回的内容来结束请求这依赖于Web应用程序的输出缓沖时怎样设置的

UpdateRequestCache 当ASPNET完成了当前的HTTP请求的处理并且输出内容已经准备好添加给输出缓沖的时候引发这个事件这依赖于Web应用程序的输出缓沖是如何设置的

BeginRequest和PreRequestHandlerExecute之间的事件是在服务器执行HttpHandler处理之前触发

PostRequestHandlerExecute和PreSendRequestContent之间的事件是在服务器执行Handler处理之后触发

下面我们看一下如何使用HttpModule来实现我们日常的应用

HttpModule通过在某些事件中注册把自己插入ASPNET请求处理管道当这些事件发生的时候ASPNET调用对相应的HTTP模块这样该模块就能处理请求了

向每个页面动态添加一些备注或说明性的文字

有的网站每一个页面都会弹出一个广告或在每个页面都以注释形式(<! >)加入网站的版权信息如果在每个页面教编写这样的JS代码的话对于大一点的网站这种JS代码的编写与维护可是一个很繁琐枯燥的工作

有了HttpModule我们就可以很简单地解决这个问题了HttpModule是客户端发出请求到客户端接收到服务器响应之间的一段必经之路我们完全可以在服务器处理完请求之后并在向客户端发送响应文本之前这段时机把这段注释文字添加到页面文本之后这样每一个页面请求都会被附加上这段注释文字

这段代码究竟该在哪个事件里实现呢? PostRequestHandlerExecute和PreSendRequestContent之间的任何一个事件都可以但我比较喜欢在EndRequest事件里编写代码

第一步创建一个类库ClassLibrary

第二步编写一个类实现IHttpModule接口

class TestModule:IHttpModule

{

public void Dispose()

{

}

public void Init(HttpApplication context)

{

}

}

第三步在Init事件中注册EndRequest事件并实现事件处理方法

class TestModule:IHttpModule

{

public void Dispose(){}

public void Init(HttpApplication context)

{

contextEndRequest += new EventHandler(context_EndRequest);

}

void context_EndRequest(object sender EventArgs e)

{

HttpApplication ha = (HttpApplication)sender;

haResponseWrite(<!这是每个页面都会动态生成的文字grayworm>);

}

}

第四步在WebConofig中注册一下这个HttpModule模块

<httpModules>

<add name=TestModule type=ClassLibraryTestModuleClassLibrary></add>

</httpModules>

name模块名称一般是类名

type有两部分组成前半部分是命名空间和类名组成的全名后半部分是程序集名称如果类是直接放在App_Code文件夹中那程序名称是App_Code

这样在Web站点是添加该类库的引用后运行每个页面会发现其源文件中都会加入<!这是每个页面都会动态生成的文字grayworm>这句话同样的方法你也可以在其中加入JS代码

身份检查

大家在作登录时登录成功后一般要把用户名放在Session中保存在其它每一个页面的Page_Load事件中都检查Session中是否存在用户名如果不存在就说明用户未登录就不让其访问其中的内容

在比较大的程序中这种做法实在是太笨拙因为你几乎要在每一个页面中都加入检测Session的代码导致难以开发和维护下面我们看看如何使用HttpModule来减少我们的工作量

由于在这里我们要用到Session中的内容我们只能在AcquireRequestState和PreRequestHandlerExecute事件中编写代码因为在HttpModule中只有这两事件中可以访问Session这里我们选择PreRequestHandlerExecute事件编写代码

第一步创建一个类库ClassLibrary

第二步编写一个类实现IHttpModule接口

class TestModule:IHttpModule

{

public void Dispose()

{

}

public void Init(HttpApplication context)

{

}

}

第三步在Init事件中注册PreRequestHandlerExecute事件并实现事件处理方法

class AuthenticModule:IHttpModule

{

public void Dispose(){}

public void Init(HttpApplication context)

{

contextPreRequestHandlerExecute += new EventHandler(context_PreRequestHandlerExecute);

}

void context_PreRequestHandlerExecute(object sender EventArgs e)

{

HttpApplication ha = (HttpApplication)sender;

string path = haContextRequestUrlToString();

int n = pathToLower()IndexOf(Loginaspx);

if (n == ) //是否是登录页面不是登录页面的话则进入{}

{

if (haContextSession[user] == null) //是否Session中有用户名若是空的话转向登录页

{

haContextResponseRedirect(Loginaspx?source= + path);

}

}

}

}

第四步在Loginaspx页面的登录按钮中加入下面代码

protected void Button_Click(object sender EventArgs e)

{

if(true) //判断用户名密码是否正确

{

if (RequestQueryString[source] != null)

{

string s = RequestQueryString[source]ToLower()ToString(); //取出从哪个页面转来的

Session[user] = txtUIDText;

ResponseRedirect(s); //转到用户想去的页面

}

else

{

ResponseRedirect(mainaspx); //默认转向mainaspx

}

}

}

第五步在WebConofig中注册一下这个HttpModule模块

<httpModules>

<add name=TestModule type=ClassLibraryTestModuleClassLibrary></add>

</httpModules>

多模块的操作

如果定义了多个HttpModule在nfig文件中引入自定义HttpModule的顺序就决定了多个自定义HttpModule在处理一个HTTP请求的接管顺序

HttpHandler

HttpHandler是HTTP请求的处理中心真正地对客户端请求的服务器页面做出编译和执行并将处理过后的信息附加在HTTP请求信息流中再次返回到HttpModule中

HttpHandler与HttpModule不同一旦定义了自己的HttpHandler类那么它对系统的HttpHandler的关系将是覆盖关系

IHttpHandler接口声明

public interface IHttpHandler

{

bool IsReusable { get; }

public void ProcessRequest(HttpContext context); //请求处理函数

}

示例把硬盘上的图片以流的方式写在页面上

class TestHandler : IHttpHandler

{

public void ProcessRequest(HttpContext context)

{

FileStream fs = new FileStream(contextServerMapPath(wormjpg) FileModeOpen);

byte[] b = new byte[fsLength];

fsRead(b (int)fsLength);

fsClose();

contextResponseOutputStreamWrite(b bLength);

}

public bool IsReusable

{

get

{

return true;

}

}

}

WebConfig配置文件

<httpHandlers>

<add verb=* path=* type=ClassLibraryTestHandlerClassLibrary></add>

</httpHandlers>

Verb属性指定了处理程序支持的HTTP动作*-支持所有的HTTP动作;GET-支持Get操作;POST-支持Post操作;GET POST-支持两种操作

Path属性指定了需要调用处理程序的路径和文件名(可以包含通配符)**aspxshowImageaspxtestaspxtestaspx

Type属性用名字空间类名称和程序集名称的组合形式指定处理程序或处理程序工厂的实际类型ASPNET运行时首先搜索bin目录中的DLL接着在GAC中搜索

这样程序运行的效果是该网站的任何一个页面都会显示wormjpg图片如何只让一个页面(defaultaspx)执行HttpHandler 中的ProcessRequest方法呢?最简单的办法是在WebConfig文件中把path配置信息设为defaultaspx

根据这个例子大家可以考虑一下如何编写验证码

IHttpHandler工厂

IHttpHandlerFactory的作用是对IHttpHandler进行管理工厂的作用请见ml

IHttpHandlerFactory接口的声明

public interface IHttpHandlerFactory

{

IHttpHandler GetHandler (HttpContext contextstring requestTypestring urlstring pathTranslated);

void ReleaseHandler (IHttpHandler handler);

}

GetHandler返回实现IHttpHandler接口的类的实例ReleaseHandler使工厂可以重用现有的处理程序实例

示例两个用IHttpHandlerFactory来实现对不同HttpHandler的调用

有两个HttpHandler将图片显示在页面上的HttpHandler和生成验证码的Handler

//将图片显示在页面上的Handler

class TestHandler : IHttpHandler

{

public void ProcessRequest(HttpContext context)

{

FileStream fs = new FileStream(contextServerMapPath(wormjpg) FileModeOpen);

byte[] b = new byte[fsLength];

fsRead(b (int)fsLength);

fsClose();

contextResponseOutputStreamWrite(b bLength);

}

public bool IsReusable

{

get

{

return true;

}

}

}

//生成验证码的Handler

class CodeHandler:IHttpHandler

{

public bool IsReusable

{

get

{

return true;

}

}

public void ProcessRequest(HttpContext context)

{

Image b = new Bitmap();

Graphics g = GraphicsFromImage(b);

SolidBrush sb = new SolidBrush(ColorWhite);

Font f = new Font(宋体 );

string str = ;

Random r = new Random();

for (int i = ; i < ; i++)

{

str += rNext();

}

gDrawString(strfsb);

bSave(contextResponseOutputStream SystemDrawingImagingImageFormatJpeg);

}

}

IHttpHandler工厂

class TestHandlerFactory : IHttpHandlerFactory

{

public IHttpHandler GetHandler(HttpContext context string requestType string url string pathTranslated)

{

string fname = urlSubstring(urlIndexOf(/) + );

while (fnameIndexOf(/) != )

fname = fnameSubstring(fnameIndexOf(/) + );

string cname = fnameSubstring( fnameIndexOf());

string className =;

className = ClassLibraryCodeHandler;

object h = null;

try

{

//h = new TestHandler();

h = ActivatorCreateInstance(TypeGetType(className));

}

catch (Exception e)

{

throw new HttpException(工厂不能为类型 + cname + 创建实例 e);

}

return (IHttpHandler)h;

}

public void ReleaseHandler(IHttpHandler handler)

{

}

}(车延禄)

配置文件

<httpHandlers>

<add verb=* path=defaultaspxdefaultaspx type=ClassLibraryTestHandlerFactoryClassLibrary></add>

</httpHandlers>

这样TestHandlerFactory就会根据请求的不同页面执行不同的HttpHandler处理程序了

HttpHandler使用会话

如果要在处理程序中使用Session那必须把该HttpHandler实现IRequiresSessionState接口IRequiresSessionState接口是个空接口它没有抽象方法只是一个标记此处就不作例子验证了

宁可去碰壁也不在家面壁缘于自然顺其自然

               

上一篇:IIS下下伪静态html(设置方法

下一篇:复杂ASP.NET服务器控件调整小技巧