ASP.NET对请求处理的过程
当请求一个*.aspx文件的时候,这个请求会被inetinfo.exe进程截获,它判断文件的后缀(aspx)之后,将这个请求转交给 ASPNET_ISAPI.dll,ASPNET_ISAPI.dll会通过http管道(Http PipeLine)将请求发送给ASPNET_WP.exe进程,在ASPNET_WP.exe进程中通过HttpRuntime来处理这个请求,处理完毕将结果返回客户端。
1. inetinfo.exe进程:
是www服务的进程,IIS服务和ASPNET_ISAPI.DLL都寄存在此进程中。
2. ASPNET_ISAPI.DLL:
是处理.aspx文件的win32组件。其实IIS服务器是只能识别.html文件的,当IIS服务器发现被请求的文件是.aspx文件时,IIS服务器将其交给aspnet_isapi.dll来处理。
3. aspnet_wp.exe进程:
ASP.NET框架进程,提供.net运行的托管环境,.net的CLR(公共语言运行时)就是寄存在此进程中。
ASP.NET Framework处理一个Http Request的流程
HttpRequest-->inetinfo.exe-->ASPNET_ISAPI.dll-->ASPNET_WP.exe-->HttpRuntime-->HttpApplication Factory-->HttpApplication-->HttpModule-->HttpHandler Factory-->HttpHandler-->HttpHandler.ProcessRequest()
ASP.NET请求处理过程是基于管道模型的,这个管道模型是由多个HttpModule和HttpHandler组成,ASP.NET 把http请求依次传递给管道中各个HttpModule,最终被HttpHandler处理,处理完成后,再次经过管道中的HTTP模块,把结果返回给客户端。我们可以在每个HttpModule中都可以干预请求的处理过程。
注意:在http请求的处理过程中,只能调用一个HttpHandler,但可以调用多HttpModule。
当请求到达HttpModule的时候,系统还没有对这个请求真正处理,但是我们可以在这个请求传递到处理中心(HttpHandler)之前附加一些其它信息,或者截获的这个请求并作一些额外的工作,也或者终止请求等。在HttpHandler处理完请求之后,我们可以再在相应的HttpModule中把请求处理的结果进行再次加工返回客户端。
HttpModule
HTTP模块是实现了System.Web.IhttpModule接口的类。
IHttpModule接口的声明:
public interface IHttpModule
{
void Init (HttpApplication context);
void Dispose ();
}
1. Init 方法:系统初始化的时候自动调用,这个方法允许HTTP模块向HttpApplication 对
象中的事件注册自己的事件处理程序。
2. Dispose方法: 这个方法给予HTTP模块在对象被垃圾收集之前执行清理的机会。此方法
一般无需编写代码。
HTTP模块可以向System.Web.HttpApplication对象注册下面一系列事件:
3. AcquireRequestState 当ASP.NET运行时准备好接收当前HTTP请求的对话状态的时候
引发这个事件。
4. AuthenticateRequest 当ASP.NET 运行时准备验证用户身份的时候引发这个事件。
5. AuthorizeRequest 当ASP.NET运行时准备授权用户访问资源的时候引发这个事件。
6. BeginRequest 当ASP.NET运行时接收到新的HTTP请求的时候引发这个事件。
7. Disposed 当ASP.NET完成HTTP请求的处理过程时引发这个事件。
8. EndRequest 把响应内容发送到客户端之前引发这个事件。
Error 在处理HTTP请求的过程中出现未处理异常的时候引发这个事件。
9. PostRequestHandlerExecute 在HTTP处理程序结束执行的时候引发这个事件。
10.PreRequestHandlerExecute 在ASP.NET开始执行HTTP请求的处理程序之前引发这个
事件。在这个事件之后,ASP.NET 把该请求转发给适当的HTTP处理程序。
11. PreSendRequestContent 在ASP.NET把响应内容发送到客户端之前引发这个事件。这
个事件允许我们在内容到达客户端之前改变响应内容。我们可以使用这个事件给页面输出
添加用于所有页面的内容。例如通用菜单、头信息或脚信息。
12. PreSendRequestHeaders 在ASP.NET把HTTP响应头信息发送给客户端之前引发这个
事件。在头信息到达客户端之前,这个事件允许我们改变它的内容。我们可以使用这个事
件在头信息中添加cookie和自定义数据。
13. ReleaseRequestState 当ASP.NET结束所搜有的请求处理程序执行的时候引发这个事
件。
14. ResolveRequestCache 我们引发这个事件来决定是否可以使用从输出缓冲返回的内容
来结束请求。这依赖于Web应用程序的输出缓冲时怎样设置的。
15. UpdateRequestCache 当ASP.NET完成了当前的HTTP请求的处理,并且输出内容已经
准备好添加给输出缓冲的时候,引发这个事件。这依赖于Web应用程序的输出缓冲是如
何设置的。
上面这么多的事件,我们看起来可能会有些眼晕,但没关系,下面一步一步地看。
HttpModule生命周期示意图
下面是事件的触发顺序:
BeginRequest和PreRequestHandlerExecute之间的事件是在服务器执行HttpHandler处理之前触发。
PostRequestHandlerExecute和PreSendRequestContent之间的事件是在服务器执行Handler处理之后触发。
多模块的操作
如果定义了多个HttpModule,在web.config文件中引入自定义HttpModule的顺序就决定了多个自定义HttpModule在处理一个HTTP请求的接管顺序。
HttpHandler
HttpHandler是HTTP请求的处理中心,真正地对客户端请求的服务器页面做出编译和执行,并将处理过后的信息附加在HTTP请求信息流中再次返回到HttpModule中。
HttpHandler与HttpModule不同,一旦定义了自己的HttpHandler类,那么它对系统的HttpHandler的关系将是“覆盖”关系。
1. IHttpHandler接口声明
public interface IHttpHandler
{
bool IsReusable { get; }
public void ProcessRequest(HttpContext context); //请求处理函数
}
示例:把硬盘上的图片以流的方式写在页面上
class TestHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
FileStream fs = new FileStream(context.Server.MapPath("worm.jpg"), FileMode.Open);
byte[] b = new byte[fs.Length];
fs.Read(b, 0, (int)fs.Length);
fs.Close();
context.Response.OutputStream.Write(b, 0, b.Length);
}
public bool IsReusable {
get
{
return true;
}
}
}
Web.Config配置文件
<httpHandlers>
<add verb="*" path="*" type="ClassLibrary831.TestHandler,ClassLibrary831"></add>
</httpHandlers>
1. Verb属性:指定了处理程序支持的HTTP动作。*-支持所有的HTTP动作;“GET”-支
持Get操作;“POST”-支持Post操作;“GET, POST”-支持两种操作。
2. Path属性:指定了这个httphandler所管辖的路径和文件名(可以包含通配符) 。
“*”、“*.aspx”、“showImage.aspx”、“test1.aspx,test2.aspx”
3. Type属性:用名字空间、类名称和程序集名称的组合形式指定处理程序或处理程序工厂的实际类型。ASP.NET运行时首先搜索bin目录中的DLL,接着在GAC中搜索。
这样程序运行的效果是该网站的任何一个页面都会显示worm.jpg图片。如果只让一个页面(default21.aspx)执行HttpHandler 中的ProcessRequest方法呢,最简单的办法是在Web.Config文件中把path配置信息设为default21.aspx。
根据这个例子大家可以考虑一下如何编写“验证码”了。
IHttpHandler工厂
4. IHttpHandlerFactory的作用是对IHttpHandler进行管理。
5. IHttpHandlerFactory接口的声明:
public interface IHttpHandlerFactory
{
IHttpHandler GetHandler (HttpContext context,string requestType,string url,string pathTranslated);
void ReleaseHandler (IHttpHandler handler);
}
l GetHandler返回实现IHttpHandler接口的类的实例,ReleaseHandler使工厂可以重用现有的处理程序实例。
示例:两个用IHttpHandlerFactory来实现对不同HttpHandler的调用。
有两个HttpHandler:将图片显示在页面上的HttpHandler和生成验证码的Handler
//将图片显示在页面上的Handler
class TestHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
FileStream fs = new FileStream(context.Server.MapPath("worm.jpg"), FileMode.Open);
byte[] b = new byte[fs.Length];
fs.Read(b, 0, (int)fs.Length);
fs.Close();
context.Response.OutputStream.Write(b, 0, b.Length);
}
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(50,20);
Graphics g = Graphics.FromImage(b);
SolidBrush sb = new SolidBrush(Color.White);
Font f = new Font("宋体", 12);
string str = "";
Random r = new Random();
for (int i = 0; i < 4; i++)
{
str += r.Next(10);
}
g.DrawString(str,f,sb,0,0);
b.Save(context.Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);
}
}
IHttpHandler工厂
class TestHandlerFactory : IHttpHandlerFactory
{
public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
{
string fname = url.Substring(url.IndexOf('/') + 1);
while (fname.IndexOf('/') != -1)
fname = fname.Substring(fname.IndexOf('/') + 1);
string cname = fname.Substring(0, fname.IndexOf('.'));
string className ="";
className = "ClassLibrary831.CodeHandler";
object h = null;
try
{
h = Activator.CreateInstance(Type.GetType(className));
}
catch (Exception e)
{
throw new HttpException("工厂不能为类型" + cname + "创建实例。", e);
}
return (IHttpHandler)h;
}
public void ReleaseHandler(IHttpHandler handler)
{
}
}
配置文件
<httpHandlers>
<add verb="*" path="default21.aspx,default22.aspx" type="ClassLibrary831.TestHandlerFactory,ClassLibrary831"></add>
</httpHandlers>
这样TestHandlerFactory就会根据请求的不同页面执行不同的HttpHandler处理程序了。
HttpHandler使用会话
如果要在处理程序中使用Session,那必须把该HttpHandler实现IRequiresSessionState接口,,IRequiresSessionState接口是个空接口,它没有抽象方法,只是一个标记。此处就不作例子验证了。