1.Asp.net工作流程和概念介绍
HttpRequest-->inetinfo.exe-->ASPNET_ISAPI.dll-->ASPNET_WP.exe-->HttpRuntime-->HttpApplication Factory-->HttpApplication-->HttpModule-->HttpHandler Factory-->HttpHandler-->HttpHandler.ProcessRequest()
HttpRuntime的ProcessRequest 方法创建HttpContext对象
HttpApplicationFactory对象负责管理一个HttpApplication对象池, 新Http请求来时, 池中有则直接分配, 没有则创建新的HttpApplication.
HttpApplication的InitModules方法创建Http Module模块, 在Http Module模块中, 它可以对HttpContext对象
实现了IHttpHandler接口的对象负责处理Http请求
http请求的处理过程中,只能调用一个HttpHandler,但可以调用多个HttpModule。
当请求到达HttpModule的时候,系统还没有对这个请求真正处理,但是我们可以在这个请求传递到处理中心(HttpHandler)之前附加一些其 它信息,或者截获的这个请求并作一些额外的工作,也或者终止请求等。在HttpHandler处理完请求之后,我们可以再在相应的HttpModule中 把请求处理的结果进行再次加工返回客户端。
2.IHttpModule 接口
namespace System.Web
{
public interface IHttpModule
{
void Dispose();
// Summary: Initializes a module and prepares it to handle requests.
// Parameters: context, An System.Web.HttpApplication that provides access to the methods, properties,
// and events common to all application objects within an ASP.NET application
void Init(HttpApplication context);
}
}
Init 方法:系统初始化的时候自动调用,这个方法允许HTTP模块向HttpApplication 对象中的事件注册自己的事件处理程序。
Dispose方法: 这个方法给予HTTP模块在对象被垃圾收集之前执行清理的机会。此方法一般无需编写代码。
3.HttpModule的例子
下面我们看一下如何使用HttpModule来实现我们的应用:HttpModule通过在某些事件中注册,把自己插入ASP.NET请求处理管道。当这些事件发生的时候,ASP.NET调用对相应的HTTP模块,这样该模块就能处理请求了。
3.1 创建HttpModule_Lzd类,实现IHttpModule接口。
namespace WebAppHttp
{
public class HttpModule_Lzd: IHttpModule
{
public void Dispose()
{
return;
}
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(context_BeginRequest);
context.EndRequest += new EventHandler(context_EndRequest);
return;
}
void context_BeginRequest(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
app.Response.Write("HttpModule_Lzd context_BeginRequest 写一些页头信息");
return;
}
public void context_EndRequest(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
app.Response.Write("HttpModule_Lzd context_BeginRequest 写一些页尾信息");
}
}
}
BeginRequest | 指示请求处理开始 |
PreRequestHandlerExecute | 在Http请求进入HttpHandler之前触发 |
PostRequestHandlerExecute | 在Http请求进入HttpHandler之后触发 |
EndRequest | 在Http请求处理完成的时候触发 |
说明:
a.BenginRequest和EndRequest分别是HttpModule容器最开始的和最后的事件;
b.BeginRequest和PreRequestHandlerExecute之间的事件是在服务器执行HttpHandler处理之前触发。
c.PostRequestHandlerExecute和EndRequest之间的事件是在服务器执行Handler处理之后触发。
在Web.Conofig中注册一下这个HttpModule模块。
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
<httpModules>
<add name="HttpModule_Lzd" type="WebAppHttp.HttpModule_Lzd,WebAppHttp"></add>
</httpModules>
</system.web>
</configuration>
这样运行每个页面,会发现其源文件中都会加入"写一些页头信息"这句话。同样的方法你也可以在其中加入JS代码。
3.2 Session验证
一般登录成功后,要把用户名放在Session中保存,在其它每一个页面的Page_Load事件中都检查Session中是否存在用户名,如果不存在就说明用户未登录,就不让其访问其中的内容。
但这种做法实在是太笨,因为几乎要在每一个页面中都加入检测Session的代码。下面使用HttpModule来减少工作量:
namespace WebAppHttp
{
public class HttpModule_Lzd: IHttpModule
{
public void Dispose()
{
return;
}
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(context_BeginRequest);
context.PreRequestHandlerExecute += new EventHandler(context_PreRequestHandlerExecute);
return;
}
void context_BeginRequest(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
app.Response.Write("写一些页头信息");
return;
}
private void context_PreRequestHandlerExecute(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
string path = app.Context.Request.Url.ToString();
int n = path.ToLower().IndexOf("login.aspx");
if (n != -1)
{
//it's login page, don't need to deal with.
}
else
{
//是否Session中有用户名,若是空的话,转向登录页。
if (app.Context.Session["User"] == null)
{
app.Context.Response.Redirect("Login.aspx", false);
}
}
return;
}
}
}
由于在这里我们要用到Session中的内容,我们只能在AcquireRequestState和PreRequestHandlerExecute事件中编写代码,因为在HttpModule中只有这两事件中可以访问Session。
这里我们选择PreRequestHandlerExecute事件 编写代码。
3.3 多个HttpModule模块
如果定义了多个HttpModule,在web.config文件中引入自定义HttpModule的顺序就决定了多个自定义HttpModule在处理一个HTTP请求的接管顺序
<httpModules>
<add name="HttpModule_Test" type="WebAppHttp.HttpModule_Test,WebAppHttp"></add>
<add name="HttpModule_Lzd" type="WebAppHttp.HttpModule_Lzd,WebAppHttp"></add>
</httpModules>
4.IHttpHandler
HttpHandler是HTTP请求的处理中心,真正地对客户端请求的服务器页面做出编译和执行,并将处理过后的信息附加在HTTP请求信息流中再次返回到HttpModule中。
HttpHandler与HttpModule不同,一旦定义了自己的HttpHandler类,那么它对系统的HttpHandler的关系将是“覆盖”关系。
namespace System.Web
{
public interface IHttpHandler
{
// Summary: Gets a value indicating whether another request can use the System.Web.IHttpHandler instance.
// Returns: true if the System.Web.IHttpHandler instance is reusable; otherwise, false.
bool IsReusable { get; }
// Summary: Enables processing of HTTP Web requests by a custom HttpHandler that implements the System.Web.IHttpHandler interface.
// Parameters: context:
// An System.Web.HttpContext object that provides references to the intrinsic
// server objects (for example, Request, Response, Session, and Server) used
// to service HTTP requests.
void ProcessRequest(HttpContext context); //请求处理函数
}
}
5.HttpHandler
namespace WebAppHttp
{
public class HttpHandler_Lzd: IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.Write("<h1><b>Hello HttpHandler</b></h1>");
}
public bool IsReusable
{
get
{
return true;
}
}
}
}
修改web.config页面
<httpHandlers>
<add verb="*" path="*" type="WebAppHttp.HttpHandler_Lzd,WebAppHttp"></add>
</httpHandlers>
Verb属性:指定了处理程序支持的HTTP动作。*-支持所有的HTTP动作;“GET”-支持Get操作;“POST”-支持Post操作;“GET, POST”-支持两种操作。
Path属性:指定了需要调用处理程序的路径和文件名(可以包含通配符)。“*”、“*.aspx”、“showImage.aspx”、“test1.aspx,test2.aspx”
Type属性:用名字空间、类名称和程序集名称的组合形式指定处理程序或处理程序工厂的实际类型。ASP.NET运行时首先搜索bin目录中的DLL,接着在GAC中搜索。
运行以后, 页面就会显示下面经过HttpModule, HttpHandler处理过的页面.
HttpModule_Lzd context_BeginRequest 写一些页头信息
Hello HttpHandler
HttpModule_Lzd context_BeginRequest 写一些页尾信息
6.IHttpHandlerFactory接口
Namespace System.Web
{
public interface IHttpHandlerFactory
{
// Summary: Returns an instance of a class that implements the System.Web.IHttpHandler interface.
// Parameters: context: An instance of the System.Web.HttpContext class that provides references to intrinsic server objects
// (for example, Request, Response, Session, and Server) used to service HTTP requests.
//
// requestType: The HTTP data transfer method (GET or POST) that the client uses.
//
// url: The System.Web.HttpRequest.RawUrl of the requested resource.
//
// pathTranslated: The System.Web.HttpRequest.PhysicalApplicationPath to the requested resource.
//
// Returns: A new System.Web.IHttpHandler object that processes the request.
IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated);
// Summary: Enables a factory to reuse an existing handler instance.
// Parameters: handler: The System.Web.IHttpHandler object to reuse.
void ReleaseHandler(IHttpHandler handler);
}
}
7.HttpHandlerFactory实例
namespace WebAppHttp
{
public class IHttpHandlerFactory_Lzd : 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 = "WebAppHttp." + cname;
//string className = "WebAppHttp.HttpHandler_Lzd";
object h = null;
try
{
// 采用动态反射机制创建相应的IHttpHandler实现类。
h = Activator.CreateInstance(Type.GetType(className));
}
catch (Exception e)
{
throw new HttpException("工厂不能为类型" + cname + "创建实例。", e);
}
return (IHttpHandler)h;
}
public void ReleaseHandler(IHttpHandler handler)
{
}
}
}
如果要使它生效, 同样要修改web.config文件, handlerfactory和handler在同样的位置写, 如下:
<httpHandlers>
<add verb="*" path="*" type="WebAppHttp.IHttpHandlerFactory_Lzd,WebAppHttp"></add>
</httpHandlers>
这样, 运行起来就会发现, 实际上这个handlerfactory创建的handler全是webpage本身的handler, 因为每个Page都实现了IHttpHandler
public class Page : TemplateControl, IHttpHandler
如果注销 string className = "WebAppHttp." + cname, 启用string className = "WebAppHttp.HttpHandler_Lzd";
那么, 则会成HttpHandler_Lzd的实例了.