Google
      
发新话题
打印

ASP.NET底层架构探索之ASP.NET管道

本主题由 CRM专家 于 2008-2-21 21:44 移动

ASP.NET底层架构探索之ASP.NET管道

HttpApplication触发事件来通知你的程序有事发生,以此来负责请求流转。这作为HttpApplication.Init()函数的一部分发生(用Reflector查看System.Web.HttpApplication.InitInternal()方法和HttpApplication.ResumeSteps()方法来了解更多详情),连续设置并启动一系列事件,包括执行所有的处理器(handler)。这些事件处理器映射到global.asax中自动生成的哪些事件中,同时它们也映射到所有附加的HttpModule(它们本质上是HttpApplication对外发布的额外的事件接收器(sink))。0 O! R$ v# M+ b  b$ h+ `6 Y0 N
HttpModule和HttpHandler两者都是根据Web.config中对应的配置被动态载入并附加到事件处理链中。HttpModule实际上是事件处理器,附加到特殊的HttpApplication事件上,然而HttpHandler是用来处理”应用级请求处理”的终点。   
# }$ R! p& L0 {; XHttpModule和HttpHandler两者都是在HttpApplication.Init()函数调用的一部分中被载入并附加到调用链上。图6显示了不同的事件,它们是何时发生的以及它们影响管道的哪一部分。
: s* H8 c* v; u) |) P) D 2 u, k7 P. Y- j0 R* T2 b6 ^4 T
图6:事件在ASP.net http管道中流转的过程) _& Q9 N0 t6 m/ K$ \6 b
HttpApplication对象的事件驱动请求在管道中流转.HttpModule可以拦截这些事件并覆盖或者扩展现有的功能。
* p3 X% O& x" i2 r; O7 ]4 c$ a( kHttpContext, HttpModules 和 HttpHandlers  3 Z& |# y( n) y
httpApplication它本身对发送给应用程序的数据一无所知-它只是一个通过事件来通讯的消息对象。它触发事件并通过HttpContext对象来向被调用函数传递消息。实际的当前请求的状态数据由前面提到的HttpContext对象维护。它提供了所有请求专有的数据并从进入管道开始到结束一直跟随请求。图7显示了ASP.NET管道中的流程.注意上下文对象(即HttpContext),这个从请求开始到结束一直都是你“朋友”的对象,可以在一个事件处理函数中保存信息并在以后的事件处理函数中取出。
; f2 R  T  m" V0 f( E) \, O3 ~一旦管道被启动,HttpApplication开始象图六那样一个个的触发事件。每个事件处理器被触发,如果事件被挂接,这些处理器将执行它们自己的任务。这个处理的主要任务是最终调用挂接到此特定请求的HttpHandler。处理器(handler)是ASP.NET请求的核心处理机制,通常也是所有应用程序级别的代码被执行的地方。记住ASP.NET页面和Web服务框架都是作为HttpHandler实现,这里也是处理请求的的核心之处。模块(module)趋向于成为一个传递给处理器(handler)的上下文的预处理或后处理器。ASP.NET中典型的默认处理器包括预处理的认证,缓存以及后处理中各种不同的编码机制。  9 u' z/ N5 e5 a( J5 Y( }
3 D7 c) |. m8 Q2 V
有很多关于HttpHandler和HttpModule的可用信息,所以为了保持这篇文章在一个合理的长度,我将提供一个关于处理器的概要介绍。
9 N/ u, x5 q0 Q' ]) e& `. nHttpModule ! X5 s1 V4 z/ v) b1 d& e
当请求在管道中传递时,HttpApplicaion对象中一系列的事件被触发。我们已经看到这些事件在Global.asax中作为事件被发布。这种方法是特定于应用程序的,可能并不总是你想要的。如果你要建立一个通用的可用被插入任何Web应用程序的HttpApplication事件钩子,你可用使用HttpModule,这是可复用的,不需要特定语应用程序代码的,只需要web.config中的一个条目。
. w# W: z; a( s2 B模块本质上是过滤器(fliter)-功能上类似于ISAPI过滤器,但是它工作在ASP.NET请求级别上。模块允许为每个通过HttpApplication对象的请求挂接事件。这些模块作为外部程序集中的类存贮,在web.config文件中被配置,在应用程序启动时被载入。通过实现特定的接口和方法,模块被挂接到HttpApplication事件链上。多个HttpModule可用被挂接在相同的事件上,事件处理的顺序取决于它们在Web.config中声明的顺序。下面是在Web.config中处理器定义。
4 Q2 X. l. I; }
<configuration>
% R+ }- r  ?  a0 Y: D1 M( z! q<system.web> ( g* {1 j! B7 Q3 g6 c. r
<httpModules> % C6 I" g7 V) X/ _: I8 R
<add name= "BasicAuthModule"
6 v3 u* S+ k+ G  p/ W  j7 d2 Ptype="HttpHandlers.BasicAuth,WebStore" /> % a) r8 u" o: d( n6 E
</httpModules> & v6 `5 M1 o1 I+ D
</system.web> " [3 l3 m/ Q( E5 a3 R! T2 _5 y/ I
</configuration>
. ]3 L. q% n8 r  @1 `, r2 H5 R
注意你需要指定完整的类型名和不带dll扩展名的程序集名。
+ {/ N1 H1 B/ S模块允许你查看每个收到的Web请求并基于被触发的事件执行一个动作。模块在修改请求和响应数据方面做的非常优秀,可用为特定的程序提供自定义认证或者为发生在ASP.NET中的每个请求增加其他预处理/后处理功能。许多ASP.NET的功能,像认证和会话(Session)引擎都是作为HttpModule来实现的。1 ~1 B& q7 P7 U% `' I7 |- P
虽然HttpModule看上去很像ISAPI过滤器,它们都检查每个通过ASP.NET应用的请求,但是它们只检查映射到单个特定的ASP.NET应用或虚拟目录的请求,也就是只能检查映射到ASP.NET的请求。这样你可以检查所有ASPX页面或者其他任何映射到ASP.NET的扩展名。你不能检查标准的.HTM或者图片文件,除非你显式的映射这些扩展名到ASP.NET ISAPI dll上,就像图1中展示的那样。一个常见的此类应用可能是使用模块来过滤特定目录中的JPG图像内容并在最上层通过GDI+来绘制“样品”字样。 ; z; z7 V9 Q1 |7 k) \
实现一个HTTP模块是非常简单的:你必须实现之包含两个函数(Init()和Dispose())的IHttpModule接口。传进来的事件参数中包含指向HTTPApplication对象的引用,这给了你访问HttpContext对象的能力。在这些方法上你可以挂接到HttpApplication事件上。例如,如果你想挂接AuthenticateRequest事件到一个模块上,你只需像列表5中展示的那样做。
. u! \  w. d: r9 Q列表5:基础的HTTP模块是非常容易实现的   f* m9 M. S. ^# l% y% Y
public class BasicAuthCustomModule : IHttpModule 2 }% v8 d5 H3 t) u+ O! A$ J9 f& M' I
{ 5 S& s9 H6 m4 V, @
 public void Init(HttpApplication application) " W' e  n  ~1 n5 r
 {
3 w, a+ D, t5 N; s) `+ G9 q// *** Hook up any HttpApplication events , F( Q' J0 k: q3 x) Y
application.AuthenticateRequest += new EventHandler(this.OnAuthenticateRequest);
% u. A$ y5 |! u6 w+ |2 d& D }
5 }' ^8 b) F1 `: V+ X9 R+ K$ g public void Dispose() { } ( U2 V" {# ]: ?4 n" {

, J, Z1 h$ U: S( ~- J1 D public void OnAuthenticateRequest(object source, EventArgs eventArgs) . O+ u$ {$ R: e' P; f
 { - J* Y1 \" f, U
HttpApplication app = (HttpApplication) source; 0 R' X3 K; Y8 w6 Q$ F
HttpContext Context = HttpContext.Current;
' _) a( p! f* a5 P1 k+ \/ e… do what you have to do… } 4 E2 h4 D0 ?) l6 Q  n
 }
1 e: U/ c! H3 L
记住你的模块访问了HttpContext对象,从这里可以访问到其他ASP.NET管道中固有的对象,如请求(Request)和响应(Response),这样你还可以接收用户输入的信息等等。但是记住有些东西可能是不能访问的,它们只有在处理链的后段才能被访问。 $ y6 B/ V( ]# e* l* {, |" z+ K+ F# Q& L
你可以在Init()方法中挂接多个事件,这样你可以在一个模块中实现多个不同的功能。然而,将不同的逻辑分到单独的类中可能会更清楚的将模块进行模块化(译注:这里的模块化和前面的模块没有什么关系)在很多情况下你实现的功能可能需要你挂接多个事件-例如一个日志过滤器可能在BeginRequest事件中记录请求开始时间,然后在EndRequest事件中将请求结束写入到日志中。 & a' X- u. M% m% q: a( F2 H, C
注意一个HttoModule和HttpApplication事件中的重点:Response.End()或HttpApplication.CompleteRequest()会在HttpApplication和Module的事件链中“抄近道”。看“注意Response.End()”来获得更多信息。
% z3 q7 S* Y/ H5 ~" k注意Response.End()
3 }! r9 e- b" e* z; L. e当创建HttpModule或者在Global.asax中实现事件钩子的时候,当你调用Response.End或Appplication.CompleteRequest的时候要特别注意。这两个函数都结束当前请求并停止触发在HTTP管道中后续的事件,然后发生将控制返回到Web服务器中。当你在处理链的后面有诸如记录日志或对内容进行操作的行为时,因为他们没有被触发,有可能使你上当。例如,sample中logging的例子就会失败,因为如果调用Response.End()的话,EndRequest事件并不会被触发。
先 做 人、后 做 事、体 验 工 作、体 验 生 活、分 享 快 乐!

TOP

发新话题