来源:http://www.microsoft.com/china/
- N$ s d1 k3 y7 ^0 ]- l/ R, z+ S2 H当 Microsoft® .NET Framework 第一次发布时,它引入了一个有突破性的 Web 服务框架,那就是 ASMX。设计 ASMX 的目的在于尽可能地简化 Web 服务的开发过程,这样即使您不是 XML 专家,也可以创建并运行 Web 服务。ASMX 是通过隐藏大多数基础 XML 和 Web 服务细节来实现这一点的。与强制开发人员直接处理 SOAP 信封和 Web 服务描述语言 (WSDL) 文件不同,ASMX 引入了自动映射层,从而实现了与传统 .NET 代码的连接。 , Y- f" | s U+ w5 \8 h7 b
ASMX 也和流行的 ASP.NET HTTP 管线紧密集成。因此,它具有传统 ASP.NET Web 应用程序的优点,例如,高级的宿主环境和进程模型、可靠的配置和部署选项,以及灵活的扩展性点。结果,ASMX 通常是大多数 Web 服务开发人员的首选。大多数开发人员错误地认为 ASMX 需要 IIS;毕竟,他们所见过的都是这种情况。但事实上,ASMX 在技术上与 IIS 并没有任何依赖关系。
- q2 m6 T& j) V) u" g* V在没有 IIS 的条件下宿主 Web 服务的需要是非常实际的。在某些环境下,可能有各种原因导致无法在必须宿主 Web 服务的计算机上运行 IIS。幸运的是,在没有 IIS 的条件下,您可以在您的进程中宿主 ASMX。自从 .NET Framework 1.0 发布以来,就可以实现这一点,但是您必须提供您的 Web 服务器来接收 HTTP 请求。Cassini 是由 ASP.NET 团队开发的一个示例 Web 服务器,它可以满足这种需要,并允许您在没有 IIS 的条件下运行 ASP 页。然而,对于大多数开发人员来说,编写他们自己的 Web 服务器或者使用诸如 Cassini 的示例 Web 服务器都是不合理的。
1 K* q% w, C) M7 }& s自从 Windows Server™ 2003 和 Windows® XP SP2 发布以后,出现了一个新的 HTTP 协议栈,名为 http.sys。通过 http.sys 和 .NET Framework 2.0 中的一些新托管类(特别是 HttpListener),您就可以轻松地为您的应用程序构建 Web 服务器,而无需在计算机上安装 IIS。这些进展使得在任何环境中运行 ASMX 成为可能。请注意,.NET Framework 2.0 当前只是测试版,因此还会有所改动。 ! V( a4 G4 y: Y& I) v9 ?) a; ]& B
- Z0 L9 t: v9 k4 S3 h# w F Y3 ZASP.NET HTTP 体系结构
; v6 R( r6 a9 T O4 C* vASP.NET 专门设计为避免依赖于 IIS。基础体系结构是由共同处理传入的 HTTP 消息的 .NET 类构成的一条管线。它被看作管线的原因是每个 HTTP 请求都要经过一系列对象,每个对象执行一些处理。
* O# j: B k# D/ }% H/ Z6 uHttpRuntime 类位于管线的前端,负责启动进程。当调用 HttpRuntime 类的静态 ProcessRequest 方法时,管线开始执行。ProcessRequest 带有一个 HttpWorkerRequest 对象,该对象包含当前请求的所有信息。HttpRuntime 使用 HttpWorkerRequest 中的信息来填充 HttpContext 对象。然后它实例化适当的 HttpApplication 类,这个类会调用注册到应用程序的任何 IHttpModule 实现以用于预处理或后期处理。此时会识别、实例化和调用适当的 IHttpHandler 实现。 $ J) D* u. f$ ~
每个进入管线的 HTTP 请求都会发生这个过程。所有 ASP.NET 功能(包括 ASMX 的功能)都包含在这些管线类中。例如,当请求到达 System.Web.Services.Protocols.WebServiceHandlerFactory 类时,就开始支持对 ASMX 终结点的处理,该类负责识别、编译(如果需要)和实例化标识的 ASMX 类,以及调用传入的 SOAP 消息的目标 WebMethod。 ! L, R/ }' R, ~6 ~
; R. p. O3 d8 o* R: R; p) Q
# E# e# C* ^6 E/ J$ @
图 1 HTTP管线和 Web 服务器
7 ~5 t/ ^2 H/ e+ A8 \ $ c+ [. [+ A* R
管线是完全自治的,与 IIS 相互独立。甚至当与 IIS 一起使用时,也是在与 inetinfo.exe 独立的进程中运行的。这个进程的名称取决于主机 OS(在 Windows XP 上为 aspnet_wp.exe,在 Windows Server 2003 上为 w3wp.exe)。除了有自己的进程模型外,管线也有独立的配置方式,与 IIS 元数据库是分开的。管线唯一没有的就是可用来接收传入的 HTTP 请求的 Web 服务器。您仍需要一些能够侦听传入的 HTTP 消息的组件,如 IIS 5.0 或 http.sys。即使是这样,这些组件也只是负责接收 HTTP 请求并将它们交给 ASP.NET 管线,这以后的任何事情都要由它来处理(请参见图 1)。
/ p8 a6 L" A1 d. y一旦该请求使其进入辅助进程,辅助进程就会创建 HttpWorkerRequest 对象(表示传入的请求)并调用 HttpRuntime.ProcessRequest 来启动管线。由于有了这样合理的设计,您就可以直接在自己的应用程序中调用 HttpRuntime。 7 D+ |" P- R4 ?8 _
( M# x! J& B1 V+ m& F- Y7 K$ V/ @- @宿主 HTTP 管线
2 j1 `3 S' |! h& H5 B/ o宿主 ASP.NET 所需要的类可以在 System.Web 和 System.Web.Hosting 命名空间中找到。开始时需要用到的类主要有 ApplicationHost、HttpRuntime 和一个从 HttpWorkerRequest 派生的类。首先调用 ApplicationHost.CreateApplicationHost。这个方法新创建一个可以处理 ASP.NET 请求的应用程序域 (AppDomain)。由于您要显式创建 AppDomain,因此在调用时必须指定虚拟目录和相应的物理目录。 / ^3 j% i+ o9 w# `+ {/ c1 b3 I. B
除了创建新的 AppDomain 以外,CreateApplicationHost 还在这个新的 AppDomain 中实例化了一个对象,您可以通过这个对象进行通讯。当进行该方法调用时,您要指定要让它实例化的类型。由于该对象将跨 AppDomain 边界使用,因此它必须从 MarshalByRefObject 派生。您可能想要使用自己的类,它具有与 AppDomain 交互所需要的方法。例如,至少您想要一个 ProcessRequest 方法,它可以提交新的 ASP.NET 请求以进行处理。 % t# c, G8 Z# T& `. @4 p4 f5 E
这里有一个类可用来实现该目的: public class MySimpleHost : MarshalByRefObject3 X7 b) M; V0 E! f$ c
{9 k1 _. Q& n/ b5 I
public void ProcessRequest(string file)# C) b1 a$ l3 A. r, P8 L& a, _
{
% C5 w9 V3 s/ v+ L* T8 M a ... // use the ASP.NET HTTP pipeline to process request
6 N2 ~3 E0 R8 t$ o& {/ U5 _ }
# c9 b) M3 G7 d; c4 X, m}
$ m8 F5 F) Q; Q9 `1 t
( \7 _+ c+ d6 ~/ z. r0 w在本例中,ProcessRequest 接受要处理的页面的文件名。在 ProcessRequest 中,您可以使用 HttpRuntime 来启动管线处理。HttpRuntime 有一个静态方法,名称也叫做 ProcessRequest,它带有一个 HttpWorkerRequest 类型的参数。 * c. s; ]% A/ J9 Q: n. [6 @
HttpWorkerRequest 是一个抽象类,但幸运的是,.NET 附带了一个简单的、名为 SimpleWorkerRequest 的派生类,它旨在处理简单的 HTTP GET 请求。当您实例化 SimpleWorkerRequest 时,必须指定要处理的页面的名称、一个可选的查询字符串和一个 TextWriter(管线将输出写入其中)。一旦您拥有 HttpWorkerRequest 对象,您就可以通过调用 ProcessRequest 来调用管线,如下所示: ... // MySimpleHost.ProcessRequest
o6 Y8 v& {1 d9 mSimpleWorkerRequest swr =
$ t# @; h! v0 c a' }2 m new SimpleWorkerRequest(page, null, Console.Out);
, i0 Z2 I" R3 c* v2 AHttpRuntime.ProcessRequest(swr);
$ R. g# g/ E4 ?) {/ @
! @3 ~; m9 K" D对于 MySimpleHost,您需要在宿主应用程序中调用 ApplicationHost.CreateApplicationHost 来实例化这个对象。然后,可以使用 MySimpleHost.ProcessRequest 将请求发送给 HTTP 管线进行处理,如以下代码片段所示: ... // console host application
0 n, ], C4 B. T; q/ {MySimpleHost msh = (MySimpleHost)
, P) H4 L q) q ApplicationHost.CreateApplicationHost(typeof(MySimpleHost), "/", " O' i( Y- A6 _9 T
Directory.GetCurrentDirectory());& ?* Z6 ~5 @: Q, X' H
foreach (string page in args)
: R. e% S. `) e msh.ProcessRequest(page);
" v/ W0 {$ Q* T( f1 i' e% @/ g3 _
0 C1 |' u& v) c" }- _: k% xApplicationHost.CreateApplicationHost 的实现期望在以下两个位置之一找到指定类型的程序集:在全局程序集缓存 (GAC) 中,或者在指定物理目录的 bin 目录中。由于没有文档化的方式可以更改这种重新实现 CreateApplicationHost 的行为缺陷,因此根据您的项目配置和部署方案,可能需要在其中的一个位置安装程序集。 3 V# b& r) e+ t! n& ~
_blank>图 2 包含宿主 ASP.NET 的整个控制台应用程序的代码。该示例可以下载。您在命令行中指定 ASP.NET 文件的名称以及一个可选的查询字符串。然后该程序会通过调用 MySimpleHost.ProcessRequest 将它们传递给管线。
) \$ m& B) \" M$ e4 L2 ]在本专栏的下载中,我提供了几个 ASP.NET 文件以供您试用,其中包括一个名为 math.asmx 的文件。当您运行应用程序时,请在命令行中指定“math.asmx WSDL”,您会看见打印在控制台窗口中的、由 ASMX 生成的 WSDL 定义(等价于通过宿主在 IIS 中的 math.asmx 浏览 http://host/math.asmx?WSDL)。如果您只在命令行中指定“math.asmx”,则它会打印由 ASMX 生成的可读的文档页面。
3 y5 s% w7 {2 ^这明显是个不实际的例子,因为您必须在命令行中指定 ASP.NET 页。在实际应用中,这个信息会通过 HTTP 请求传入。为了支持 HTTP,您需要在应用程序中集成一个 Web 服务器。 7 r7 m' F2 ]3 t4 E" s- X, x
/ F& [* z1 [4 B5 B" U别了,IIS ' i$ G; M$ E/ n5 N ]2 A/ c
Http.sys 是一个新的低级 HTTP 协议栈,可在 Windows Server 2003 和 Windows XP SP2 中使用。Http.sys 是一个内核模式组件,它为计算机中的所有应用程序提供它的 HTTP 服务。这意味着 HTTP 支持深深依赖于 OS。甚至 IIS 6.0 也进行了重新架构,以便可以使用 http.sys(请参见图 3)。
& o, Z6 w# G" H3 B
, p( U, X* U+ D; C4 c8 p1 ]) {/ q. G8 s/ \
图 3 Http.sys 体系结构 0 _5 z* f' I' R2 m' w7 @
V6 d& C, [) w5 |/ O1 U9 y
在 6.0 版之前,IIS 依靠 TCP/IP 内核和 Windows Sockets API (Winsock) 接收 HTTP 请求。由于 Winsock 是一个用户模式组件,因此每个接收操作都需要在内核模式和用户模式之间进行切换。现在 Http.sys 可以直接在内核中缓存响应。当处理缓存的响应时,将 HTTP 栈放在内核中可以使得移除代价昂贵的上下文切换成为可能,从而提高效率和整体吞吐量。 2 L. I% G" x; t0 L. B$ i% k1 i8 D7 w
当 http.sys 接收到请求时,它可以直接将该请求转发到正确的辅助进程中。另外,如果辅助进程无法接受该请求,http.sys 会存储该请求,直到辅助进程启动并可以接受它为止。这意味着辅助进程失败不会中断服务。当 IIS 6.0 启动时,WWW 服务会与 http.sys 进行通讯,并为配置的每个 IIS 应用程序注册路由信息。无论您何时在 IIS 中创建应用程序或移除应用程序时,WWW 服务都会与 http.sys 进行通讯以更新它的路由信息。 # y" r/ ?# \1 }; [3 y" E
正如您在图 3 中所看到的,http.sys 为 IIS 6.0 Web 体系结构奠定了基础,但它没有以任何方式与 IIS 产生联系。运行在计算机中的任何应用程序都可以利用 http.sys 来接收 HTTP 请求。与 WWW 服务相似,您可以用 http.sys 注册应用程序,并开始侦听传入的 HTTP 请求。.NET Framework 2.0 引入了一套托管类,使得这些托管类可以很容易地实现该操作。 * Z% ]' W8 ]- K6 r# N* F
; M+ z+ @: l) F' E2 E" s0 Y" L
HttpListener:实现您自己的 Web 服务器
4 t( O9 ?( ?) V. t4 cSystem.Net 包含几个用来与 http.sys 进行交互的新类。HttpListener 是这些类中的关键一个。可以使用它来创建简单的 Web 服务器(或侦听器),用于响应传入的 HTTP 请求。这个侦听器在 HttpListener 对象的生存期内都保持活动状态,不过您可以通过命令通知它开始和停止侦听。
; H: ^/ B0 v* I( [- H' o5 R* V要使用 HttpListener,必须先对它进行实例化。然后通过向 Prefixes 属性中添加 URL 前缀,来指示侦听器应该处理哪些 HTTP URL。每个 URI 必须包含一个方案(“http”或“https”)、主机、端口(可选)和路径(可选)。每个前缀都必须以正斜杠结尾: HttpListener listener = new HttpListener();' A9 ^# K( n6 y# [2 B
listener.Prefixes.Add("http://localhost:8081/foo/");
! Z- w0 R, v+ c9 k7 U! @/ c: `* A6 Blistener.Prefixes.Add("http://127.0.0.1:8081/foo/");
' w" l5 S3 g' _2 w; G4 {2 Clistener.Start();
/ T6 f* K0 Z g% J l& K
$ t3 E% `% j5 K% }# a( N对于一个特定的 URL 前缀,只能有一个 HttpListener 在侦听它。如果您试图添加副本,就会得到 Win32Exception 异常。当您指定一个端口时,可以将主机名替换为“*”,以指示侦听器应该处理具有该端口的所有 URI,除非另一个 HttpListener 与它们匹配。或者可以将主机名替换为“+”,以指示侦听器接受对指定端口的所有请求,如下所示: |