动手写这个系列之前,我犹豫不决,因为类似的文章在博客圆中,已经不少了,感觉有点不好意思:)园中,有位朋友说过这样一句话,“我们不要重复发明轮子,而要扩展轮子”,那我就当自己是在“扩展轮子“,给自己找个由头吧!
在我的博客中,第一篇写的就是asp.net运行时流程,园子里的朋友给了很多的宝贵意见,在此谢过了。也有朋友说,太抽象了,不够细。所以我在工作之余,抽出时间写下这点东西!
还是先贴一张图,所谓一图胜万言,使大家对运行时,有一个全局的把握。
图很明了,三个椭圆,不,应该是四个,第一个是asp.net的运行环境,当请求到达www服务inetinfo.exe后,经过aspnet_isapi.dll筛选之后,请求这才真正进入aspnet_wp.exe进程,也就是httpRuntime,之后系统会加载一系列的HttpModule,那么我们就来看看系统到底加载了哪些模块。先定位到“C:"WINDOWS"Microsoft.NET"Framework"v2.0.50727"CONFIG“,怪了,这个目录很是熟悉,是的,这是.net系统配置文件machine.config所在的目录。不过我今天要找的不是他,而是web.config,这是.net专门给web独立定义connfig文件,没错,web.cofig也在此目录中。
打开web.config文件后,大家可能很郁闷,这么多的数据,怎么看呢!大家别急,都知道web.config文件是合法的xml文件,那我们就把扩展名config改成xml得了,再用IE打开,数据就一目了然了!
很明显,Congfiguration根,包括注释一起,才四个主元素,我们打开system.web节点,
也很明显,可以看到系统对“输出缓存”,“会话”,“验证和授权”等模块都一一进行配置加载。记得有朋友问,为什么在自定义的httpModule中,使用Session[“key”]的时候都会抛出null对象异常呢?我告诉大家,那是因为Session在此时,还没有生成,所以在自定义的httpModule中使用Session,那都是瞎忙活!
有人可能会问,说了这么多,自定义的HttpModule到底有什么用呢?先别急,我们先来看看系统的IHttpModule接口的真面目!
[AspNetHostingPermission(SecurityAction.InheritanceDemand, Level=AspNetHostingPermissionLevel.Minimal),
AspNetHostingPermission(SecurityAction.LinkDemand, Level=AspNetHostingPermissionLevel.Minimal)]
public interface IHttpModule
{
// Methods
void Dispose();
void Init(HttpApplication context);
}
很明显,从字面意思,就可以看出这两个方法的作用, Init()用来初始化一个module,传递一个HttApplication对象作为参数,为以后捕获处理请求做一些准备,Dispose()用来销毁不再被module所使用的资源!注意,运行时的大部分方法都是调用HttApplication对象的方法,严格说,应该是向HttApplication对象订阅。
下面我们就动手来写一个自己的HttpModule!
第一步,新建一类库!
class MyHttpModuleClass:System.Web.IHttpModule//实现IHttpModule接口
{
public MyHttpModuleClass()
{ }
public void Init(HttpApplication application)//实现Init方法
{
application.BeginRequest+=new EventHandler(this.application_BeginRequest);
application.EndRequest+=new EventHandler(this.application_EndResponse);
}
private void application_BeginRequest(Object obj, EventArgs e)
{
HttpApplication application = (HttpApplication)obj;
HttpContext context = application.Context;
HttpRequest request = context.Request;
HttpResponse response = context.Response;
response.Write("hi ,i am from application_BeginRequest"+"</br>");
}
private void application_EndResponse(Object obj, EventArgs e)
{
HttpApplication application = (HttpApplication)obj;
HttpContext context = application.Context;
HttpRequest request = context.Request;
HttpResponse response = context.Response;
response.Write("hi ,i am from application_EndResponse");
}
public void Dispose()//实现Dispose方法,但什么也不做
{ }
}
第二步
新建一website,在她的web.config中,加上httpModules节点!
<httpModules>
<add name="wmjHttpModule" type="MyHttpModule.MyHttpModuleClass"/>
</httpModules>
注意wmjHttpModule为别名,可以随便起,MyHttpModule为程序集名称,MyHttpModuleClass为自定义的IHttpModule类名!
第三步
编译类库,然后把dll拷贝到GAC,或者website的bin目录下面,如果是直接放bin目录下,那么配置文件就没必要加上程序集的名称了
<httpModules>
<add name="wmjHttpModule" type="MyHttpModuleClass"/>
</httpModules>
第四步
按F5,您是不是看到这两句话了呢?
hi ,i am from application_BeginRequest
hi ,i am from application_EndRequest
上一篇中,通过实现IHttpModule接口,就看到网页上输出了两句话,这是怎么回事呢?先别急,通过反射,先看看IHttpApplication接口到底定义了那些事件!
My god,这么多事件!
在此,先弄清楚一个先后顺序问题,向IHttpModule订阅的事件处理方法,并不一定优先于IHttpHandler的方法先执行,比如IHttpHandler.ProcessRequest方法优先于IHttpModule.EndRequest事件处理方法,这很明显!在HttpModule中的主要任务就是向HttpApplication发出订阅,至于订阅的处理方法,什么时候执行,得看请求执行到了哪里,所以如果要实现自定义的IHttpModule,只要做两件事;订阅和实现订阅的处理方法!(谢谢Cat Chen的提醒)
在上一篇中,我只实现了BeginRequest和EndRequest方法,看看系统中是怎样定义的
// 摘要:
// 在 ASP.NET 响应请求时作为 HTTP 执行管线链中的第一个事件发生。
EndRequest定义
// 摘要:
// 在 ASP.NET 响应请求时作为 HTTP 执行管线链中的最后一个事件发生。
至于IHttpApplication的其他方法,在后面的环节中再说。
这时候,突然想到一件事,那时我刚接触网络,朋友帮我申请了一个免费的空间,我做了一个人主页放到空间里!出了一怪事(当时认为),在我的个人主页的页眉和页脚上,莫名其妙的被别人加上了一些情色广告!当时很纳闷,怎么也弄不掉那些广告,但是查看html的源文件,压根看不到任何异常的demo!后来才知道运营商为了自己的利益,在IHttpModule模块上做了手脚!
(HttpModule应用)还是动手做一个Moduled的例子吧!
曾经看到园子中有朋友,在自定义的Module中,订阅了授权事件HttpApplication.OnAuthorizeRequest,然后在授权事件的处理方法中,从Session[“key”]集合中获取用户标识ID,并和当前请求的用户标识ID比较,如果与Session[“key”]集合中的用户标识ID相同,就授权,不同或者没有用户标识ID,就不授权!当然,这种基于URL授权的想法不错!
但是,有几点需要考虑到!(说明不是针对那为朋友,只是找个案例罢了......)
一,OnAuthorizeRequest事件的处理方法中,session集合还没有生成,又怎能取值呢?
二,从数据库中查找用户ID,性能肯定有损耗,有人可能会想到,我只在登录的时候查询一次数据库,然后保存起来,哈哈,不过还是有点麻烦,因为此时cache和session等都不能用,只有application可以使用,难道把用户ID保存在HttpApplication对象里吗?(HttpApplication不是隔离的,怎感觉不妥)
三,解决办法,目前只是猜想,哈哈,思路是这样的!
在HttpApplication. OnAuthorizeRequest事件的处理方法之前,或者之中,想办法提前获取请求关联的状态,这里指会话状态(session)!哈哈
现在,我就在OnAuthorizeRequest事件的处理方法中,做一些简单的处理,判断请求的类型,如果不为get或者post类型,就终止这个Http管道中的其他事件,直接跳到EndRequest事件!
配置,就不说了
class AuthorHttpModule : System.Web.IHttpModule//, IRequiresSessionState
{
public AuthorHttpModule()
{ }
public void Init(System.Web.HttpApplication application)
{
application.AuthorizeRequest += new EventHandler(this.OnAuthorize);
application.AcquireRequestState += new EventHandler(application_AcquireRequestState);
}
public void OnAuthorize(Object sender,EventArgs e)
{
HttpApplication application = (HttpApplication)sender;
HttpContext context = application.Context;
HttpRequest request = context.Request;
HttpResponse response = context.Response;
string type = request.HttpMethod;
if (type.ToLower() == "get" || type.ToLower() == "post")
{ }
else
{
application.CompleteRequest();
response.StatusCode = 500;
response.StatusDescription = "SORRY,Request's type must be Get or Post!";
}
HttpApplicationState app1=context.Application;
HttpApplicationState app2 = application.Application;
App1["app1"] = "appValue1";
App2["app2"] = "appValue2";
HttpSessionState sessionState = context.Session;
//sessionState["ss1"] = "sss";//here ,throw Exception object
}
void application_AcquireRequestState(object sender, EventArgs e)
{
HttpApplication application = (HttpApplication)sender;
HttpContext context = application.Context;
HttpRequest request = context.Request;
HttpResponse response = context.Response;
HttpSessionState sessionState = context.Session;
sessionState["ss"] = "ssValue";
}
public void Dispose()
{ }
}
说明;
一,application.CompleteRequest()方法终止这个Http管道中的其他事件,直接跳到EndRequest事件!然后设置响应的状态代码为500!
二,HttpApplicationState app1=context.Application和HttpApplicationState app2 = application.Application;方法都是获取服务器的HttpApplicationState集合!而且,在授权事件中可以获取或者设置,但是HttpSessionState集合不行!(但是在AcquireRequestState事件中可以获取或者设置HttpSessionState集合)!
最后;
虽然application.CompleteRequest()方法终止了Http管道中的其他事件,直接跳到EndRequest事件,但是后面的application.PreSendRequestHeaders和application.PreSendRequestContent事件仍然会执行,不信试试!
虽然我们自定义了自己的Module,但并不会覆盖系统默认的Module,顺序是先系统的,后自己定义的,如果想完全清除系统的Module,在config中remove掉对应的项就OK!
原文出处:
http://www.cnblogs.com/wmj/