下载本文中的代码: ServiceStation0601.exe (115KB)
![*](http://img.microsoft.com/library/gallery/templates/MNP2.Common/images/3squares.gif)
Microsoft® .NET Framework 2.0 版通过几个有趣但可能令人困惑的方式重塑了 Web 服务的前景。因此,本月我打算解答一些与 ASP.NET Web Services (ASMX) 2.0、Web Services Enhancements (WSE) 3.0 和 Windows® Communication Foundation (WCF)(WinFX® 的一部分)有关的最常见问题。
问:能解释一下支持接口的新 ASMX 2.0 的工作方式吗?哪些属性用于接口,哪些属性用于类?
答:.NET Framework 2.0 中的 ASMX 框架引入了一个重大的编程模型改进:能够在 .NET 接口定义中定义 Web 服务协定。此功能允许您使用常规的 System.Web.Services 属性([WebServiceBinding]、[WebMethod]、[SOAPDocumentMethod] 等)来批注 .NET 接口定义。然后,您就可以在 .NET 类上实现 .NET 接口,以有效地实现服务协定。
该方法与 WCF 中的方法(主要区别是属性名不同)十分类似。它允许您从实现代码中去耦服务协定,从而使管理和重用变得更加方便,也使代码的可读性更高。图 1 显示该方法的一个示例。
如图 1所示,[WebServiceBinding] 和 [WebMethod] 用于接口,而 [WebService] 用于类。使用此模型时,影响服务协定定义的所有属性都必须置于接口定义(而不是类)中。这包括 [SOAPDocumentService]、[SOAPDocumentMethod] 以及能够应用于方法签名的各种 System.XML.Serialization 属性。您不能跨接口和类定义混合和匹配这些属性,ASMX 将帮助您确保这一点。
例如,一旦您从接口派生了一个包含这些服务协定属性的类之后,ASMX 将在运行时检查该类以确保您没有使用可能会在将来更改服务协定的任何附加属性。如果 ASMX 发现了此类附加属性,它将显示一个错误,指出您违反了模型(参见图 2)。
![](https://i-blog.csdnimg.cn/blog_migrate/eaa68b2a6d1d4cecb7ad91f6c86c7e6d.png)
图 2 ASMX 服务属性用法错误
虽然这最小化了需要在派生类上使用的属性数量,但仍有一些需要用于配置服务终结点。您将在类上使用 [WebService] 来指定终结点的 详细信息。这很有意义,原因是您可以让多个服务实现同一个服务协定。同样,如果您使用 WSE 3.0,则应在类上使用 [Policy] 将安全策略配置应用于特定的终结点。
但在某些情况下,如果需要通过各种 [WebMethod] 属性(BufferResponse、CacheDuration、EnableSession、TransactionOption)来配置本地处理行为,您可能还需要在派生方法上使用 [WebMethod]。例如,在图 3 所示的代码中,我使用 [WebMethod] 来配置响应缓存。
此处使用 [WebMethod] 不会影响服务协定。它只配置本地处理行为。在该上下文中使用 [WebMethod] 时,您只能使用这些行为属性,并且必须避免能够修改服务协定的任何属性。
问:如何在自定义应用程序中承载 ASMX 2.0 类(不使用 IIS)?
答:在 MSDN®Magazine 2004 年 12 月的专栏 (Service Station: Run ASMX Without IIS) 中,我讨论了如何利用 HTTP.sys 和 .NET Framework 2.0 中新的 HTTPListener 类在自己的应用程序中宿主 ASP.NET 管道。这是一个重要的任务,也是在自己的应用程序中于 IIS 外部重用 ASMX 类的唯一方式。但 WSE 3.0 与之完全不同。
WSE 3.0 通过它的消息处理层和宿主模型合并了新的和改进后的 ASMX 2.0 编程模型。这可以通过任何支持 WSE 的传输(如 TCP)在自己的应用程序中宿主 ASMX 终结点。这意味着,现在我可以在 Windows 服务、Windows 窗体应用程序甚至控制台应用程序中承载如图 1所示的 ASMX 代码。代码很简单 — 只需调用 SOAPReceivers.Add 并提供 ASMX 类即可,如下所示:
class Program { static void Main(string[] args) { Uri uri = new Uri("SOAP.tcp://localhost:9393/quoteservice"); SOAPReceivers.Add(uri, typeof(QuoteService)); Console.WriteLine("Listening..."); Console.ReadLine(); } }
这与 WCF 模型非常类似,您可以使用 ServiceHost 在任何宿主环境中承载服务类型。但您先不要激动,我应该指出,您不能像对待 WCF 中的 ServiceHost 那样将 HTTP 地址提供给 SOAPReceivers.Add。SOAPReceivers 不提供与 HTTP.sys 的直接集成,而这正是本例所需的。因此,要通过 HTTP 承载 ASMX 服务,您必须转换为 映射或者自己编写 HTTP.sys 集成代码。
问:WCF 为开发人员提供了哪些 ASMX 2.0 和 WSE 3.0 所没有的功能?
答:ASMX 2.0 和 WSE 3.0 提供了许多重要功能,但某些开发人员错误地认为这些功能只属于 WCF。例如,它们的堆栈都提供了类似的基于属性的编程模型,其中,您可以根据 .NET 接口定义编写服务协定(参见本专栏的第一个问题和解答)。它们的堆栈都提供与传输方式无关的 SOAP 实现,可以随附多个传输信道和自定义传输挂钩。它们的堆栈都允许在任何基于 Windows 的应用程序中承载服务(通过 WSE 中的 SOAPReceivers 或 WCF 中的 ServiceHost)。它们的堆栈都提供多个消息编码方式,包括两个最广泛受支持的编码方式:XML 1.0 和消息传输优化机制 (MTOM)。它们的堆栈都支持基于消息的安全性,并且可通过简单的配置元素进行配置(能够立即启用的安全性配置文件)。图 4 中的表格总结了堆栈的比较方式。
堆栈的主要不同之处在于它们对各种 WS-* 规范的支持。WSE 3.0 仅支持安全框架,而 WCF 支持安全框架、可靠消息处理框架和事务框架。如表格所示,WCF 还提供更多的本地处理行为和自定义挂钩。实际上,WCF 对象模型中的每一层都可以通过代码、属性或配置进行扩展。
最终,WCF 提供更完整的开发框架,该框架是围绕各种 Web 服务协议从头开始设计的。现在,如果使用 ASMX 2.0 和 WSE 3.0,您仍然可以获得非常类似的体验。此外,未来迁移的前景也很看好。
问:将 ASMX 代码移植到 Windows Communication Foundation 困难吗?
答:当然不困难,除非您认为更改属性名很困难。将 ASMX 代码移到 WCF 是一个机械过程,如果需要,您甚至可以在一定程度上自动化。该过程需要将各种 System.Web.Services 和 System.Web.Services.Protocols 属性转换为等效的 System.ServiceModel 属性。图 5 显示一个示例,说明如何将图 1中的 ASMX 代码移植到 WCF。
这两个框架都支持对类定义或单独的接口定义进行批注,因此额外的重构并不是必需的,但可能有这方面的需要。此外,通过使用 FormatMode=ContractFormatMode.XMLSerializer,如果不需要,您则不必将所有消息类型移植到新的 [DataContract] 模型。随着 WCF 发布日期的临近,Microsoft 将公布更多详细的迁移细节。
问:何时应该将 WSE 与 ASMX 结合使用,何时应该单独使用 ASMX?我希望将这个问题弄得更清楚些。
答:ASMX 2.0 支持 WS-I Basic Profile 1.1 和 SOAP 1.2。这意味着,它支持 XML 1.0、XML 架构定义 (XSD)、Web 服务描述语言 (WSDL)、SOAP 1.1、SOAP 1.2 以及编译时的基本配置文件一致性验证。WSE 3.0 通过提供对某些更高级的 WS-* 协议的支持,来补充 ASMX 2.0 的功能。目前,当您需要使用以下一个或多个功能来增强 ASMX 服务时,通常会使用 WSE 3.0:
• | 基于消息的安全性 (WS-Security) |
• | 高效的二进制数据传输 (MTOM) |
• | 可选的宿主环境 |
• | 自定义声明性策略管道 |
基于消息的安全性和 MTOM 是人们在 ASMX 项目中启用 WSE 的两个主要原因。还有一个好消息是,您不必修改服务逻辑就可以这样做。WSE 3.0 通过 ASMX 提供的扩展点来利用 ASMX。这使您能够利用各种 WSE 3.0 功能,而不必学习新的编程模型 — 您需要做的就是运行 WSE 设置工具并选择所需的选项。
在 ASMX 项目中启用 WSE 3.0 的另一个原因是,要利用它的策略管道扩展性模型。WSE 使您能够插入对进入和离开终结点的 SOAP 消息执行预处理和后续处理的筛选器。该扩展点比 ASMX SOAPExtension 框架更易于使用,因为它提供的功能可以创建能够与 WSE 提供的现有声明性安全策略相结合的自定义声明性策略。您决定“启用”WSE 可能只是为了具有该扩展选项,或者至少是为了使用改进的 SOAP 消息诊断跟踪功能。
有了 WSE 2.0,大多数开发人员在需要进行自定义宿主时就抛弃了 ASMX。现在,WSE 3.0 使您能够执行 ASMX 类型的自定义宿主,这就更没有问题了。导致您单独使用 WSE 消息处理 API 的主要情况是,需要实现可选的消息交换模式,例如,双工通信、多播或发布/订阅情况。消息处理类 SOAPSender 和 SOAPReceiver 使您能够实现任何类型的高级消息处理模式。
问:当我在 Web 站点中启用 WSE 3.0 后,在配置文件中再也看不到 WSE SOAPExtension 了。相反,我看到了一个 SOAPServerProtocolFactory。那是什么?
答:默认情况下,可以使用几个不同的服务器协议调用 ASMX 终结点,包括 HTTPSOAP、HTTPSOAP12、HTTPGet、HTTPPost、HTTPPostLocalhost 和 Documentation。您可以在 Web.config 中启用或禁用其中任何一个协议。.NET Framework 2.0 中的默认 ASMX 配置如下所示:
<WebServices> <protocols> <clear /> <add name="HTTPSOAP12" /> <add name="HTTPSOAP" /> <add name="HTTPPostLocalhost" /> <add name="Documentation" /> </protocols> ... </WebServices>
每个协议都期望不同类型的 HTTP 消息,并实现不同的 ASMX 调用协议。例如,对于 HTTPSOAP,传入的 HTTP 有效负载将始终包含 ,而对于 HTTPGet,数据在查询字符串中传入且 HTTP 有效负载为空。前者在响应中返回 SOAP 信封,而后者只返回纯 XML。
为了处理这些差异,ASMX 处理程序 (System.Web.Services.Protocols.WebServiceHandler) 使用名为 ServerProtocol 的类来处理传入的消息。还有几个派生自 ServerProtocol 的类,包括 SOAPServerProtocol 和 HTTPGetServerProtocol。这些类将映射到配置文件中的不同协议。ASMX 处理程序会检查传入的 HTTP 消息,并选择合适的 ServerProtocol 类型。从那时起,ServerProtocol 实例就开始管理调用操作的过程。在调用过程中,SOAPExtension 框架就是它管理的内容之一。
SOAPExtension 框架是一个扩展点,以便开发人员在 ASMX 处理程序和目标 WebMethod 之间插入代码。实际上,它提供了一种用于转换传入消息流和传出消息流的机制。如果您需要修改 SOAPExtension 中的消息,则必须提供一个将传递到链中下一个扩展的新流(在一开始即与 SOAPExtension.ChainStream 绑定在一起)。因此,将重复以下过程:SOAPExtension 接收流,进行分析,执行它的任务,然后写入下一个 SOAPExtension 将读取的流。
WSE 2.0 完全作为 SOAPExtension 而实现的。在该级别实现 WSE 的一个问题是,它会在流级别导致大量的序列化/反序列化系统开销。移到 WSE 3.0,WSE 小组希望通过将抽象层上移并编写自己的 SOAPServerProtocol 实现来优化他们的 SOAP 处理模型。因此,他们询问 ASMX 小组是否能够通过 ASMX 2.0 中的配置来替换默认的 SOAPServerProtocol。ASMX 开发人员提供一个名为 的新配置元素,任何人都可以使用它来提供自己的实现(虽然我们不鼓励这么做)。ASMX 处理程序会读取该类型,并用它来创建相应 SOAPServerProtocol 类型的实例以处理新的请求。WSE 3.0 将使用该挂钩,并使用 Microsoft.Web.Services3.WSEProtocol 来替换默认的 SOAPServerProtocol 类。
WSEProtocol 可以在同一个类中管理 SOAPExtension 机制和 WSE 管道,从而减少了一些流摩擦。WSE 3.0 仍然使用 SOAPExtension(名为 WSEProtocolExtension),但它从来不在配置文件中出现,这是因为 WSEProtocol 在启动时动态插入它(以确保它始终存在)。WSEProtocolExtension 只负责识别和分析 MTOM 和 XML 1.0 流;其他事情均由 WSEProtocol 实现来完成。
因此,简单说, 是 WSE 3.0 将自身融入 ASMX 2.0 的方式。WSE 3.0 仍然使用 SOAPExtension,在该版本中,它只是不在执行任务的决策地。
问:如果在我自己的应用程序中承载 ASMX 类,会对 HTTPContext.Current 造成什么影响?SOAPExtension 又如何呢?
答:遗憾的是,ASMX 设计与 ASP.NET 紧密地耦合在一起。例如,当 Visual Studio® 创建新的 ASMX 类时,在默认情况下会从 System.Web.Services.WebService 派生。这会通过新的 ASMX 类上的属性来公开各种 System.Web.HTTPContext 属性,从而鼓励它们在 WebMethod 中的使用。某些开发人员还习惯于通过 HTTPContext.Current 显式使用 HTTP 上下文。
当您使用 WSE 3.0 中的 SOAPReceivers 承载一个 ASMX 类时,这种类型的代码就不再工作了。本例,您的代码在 IIS 和 ASP.NET HTTP 管道之外执行。HTTPContext.Current 将始终为空,因此这可能要求您修改现有的 WebMethod 逻辑。通常,如果您打算在 ASP.NET 之外的宿主环境中重用该类型,则应该避免在 WebMethod 代码中依赖 HTTP 管道中的任何内容。
此外,在使用 SOAPReceivers 作为宿主时,就不能再依赖 HTTPModule 或 SOAPExtension 为您执行任务了。因为现在您在 ASMX 处理程序之外执行,所以这些媒介将不再起作用。
问:我在 WSE 3.0 设置工具中找不到 Filters 选项卡。如何访问它呢?
答:Filters 选项卡不再位于 WSE 3.0 设置工具中,因为 元素已经从 WSE 3.0 配置部分中移除了。在 WSE 2.0 中,您使用 元素来定义通过筛选器构建管道的方式。默认情况下包含的一个筛选器是策略筛选器。策略筛选器查看另一个配置文件(策略文件),以便根据其断言(类似于筛选器)来决定如何处理传入和传出的消息。
在 WSE 3.0 中,这两个概念已统一。因此,现在的策略由断言组成,并且每个断言都会在管道中插入 WSE 筛选器。您将断言放到策略中,以控制筛选器管道在运行时如何构建和组织。因此,如果您希望在管道中插入自定义筛选器,则应该编写筛选器类,编写使用新筛选器的策略断言类,在策略中使用断言类,然后将该策略应用于服务。在 WSE 3.0 中,正是策略框架驱动着管道,因此就不再需要 部分。
问:WSE 3.0 支持哪些传输?编写自定义传输很难吗?
答:WSE 3.0 支持 HTTP 和 TCP。WSE 提供一个自定义传输框架,以允许第三方插入自己的传输实现。自从 WSE 2.0 发布以后,一些开发人员提供了示例 WSE 传输来演示如何完成此操作。其中一个可用于 Microsoft 消息队列服务器 (MSMQ)(请参阅 SOAPMSMQ Transport)、SMTP(请参阅 SOAP.SMTP://)和 UDP(请参阅 SOAP.Udp 0.1)。所有这些传输均针对 WSE 2.0 编写,但是将它们移植到 WSE 3.0 也没什么问题。
以下代码提供一个使用 SMTP 传输在控制台应用程序中宿主服务的示例:
SOAPReceivers.Add(new Uri("SOAP.SMTP://test@skonnard.com"), typeof(ExpenseReportService)); Console.ReadLine();
然后,在客户端,您只需在调用方法前指定 SMTP 地址即可,如下所示:
ExpenseReportServiceWSE svc = new ExpenseReportServiceWSE(); svc.Url = "SOAP.SMTP://test@skonnard.com"; svc.Submit(report);
现在,客户端将向 test@skonnard.com 电子邮件地址发送消息,接收器将从同一个地址接收消息。该示例中比较有趣的是,客户端和接收器不再需要同时运行。请注意,在使用自定义传输时,开发人员体验不会受到影响。您必须做的一件事是,通过将 映射添加到 WSE 配置部分来告诉 WSE 如何处理“SOAP.SMTP”协议方案,如下所示:
<Microsoft.Web.services3> <messaging> <transports> <add scheme="SOAP.SMTP" type="SOAPSMTPTransport,SOAPSMTP"/> </transports> </messaging> ... </Microsoft.Web.services3>
该元素将协议方案映射到一个从 ISOAPTransport 派生的类。当调用 SOAPReceivers.Add 时,它会从配置部分查找映射到指定协议方案的类型。然后,它会实例化指定的类型,并使用 ISOAPTransport 方法生成输入和输出通讯信道。客户端也会发生同样的操作。这些信道会对其余 API 隐藏所有传输实现的细节。
因此,编写自定义 WSE 传输需要实现三个类:一个用于建模传输 (ISOAPTransport),另外两个用于建模输入/输出信道(ISOAPInputChannel 和 ISOAPOutputChannel)。传输可提供信道并管理所有信道共享的资源,而信道主要用于通过传输发送/接收消息。这里,您处理的是低级别传输细节,因此有点困难。了解其工作方式的最佳方法是,演练我在前面提到的现有 WSE 传输示例。
问:我能使用“Add Web Reference”为自定义宿主的 WSE 3.0 服务生成代理吗?
答:如果您在使用 TCP 进行通讯的 Windows 服务中承载服务,则检索 WSDL 的唯一方法就是使用 WSEWSDL3.exe 工具。该工具会向请求其元数据的服务发送 SOAP 消息,并根据在 SOAP 响应消息中返回的 WSDL 生成代理。
但是,如果相同的服务也通过 ASP.NET (HTTP) 来承载,则可以使用 Visual Studio 中的标准“Add Web Reference”命令(或 WSDL.exe)来生成代理(在此之前,请确保您在客户端应用程序中启用了 WSE,以便获得 WSE 代理)。生成的 WSE 代理可以用于任何 WSE 传输,而不仅仅是 HTTP。只需通过 Url 或 Destination 属性将代理的地址更改为 TCP 地址,它就可以像通过 WSEWSDL3.exe 生成的代理一样工作了。
问:应该如何组织解决方案,以便在不同的宿主间共享 ASMX 类型呢?
答:如果您需要在不同的宿主间共享 ASMX 类型,则应该将 ASMX 类放到共享类库中。起初,这看上去有些奇怪,因为您的 ASMX 代码可能始终在 Web 项目中,但是它们也可以分解到库中。我甚至可以更进一步,将所有消息序列化类组织到它们自己的单独类库中,因为您可能希望与客户端和代码生成器共享这些类型(例如,使用架构导入程序扩展)。
通过该配置,只需在每个宿主项目(包括 Web 站点宿主)中添加对类库的引用。然后,您的 Web 站点宿主将不再需要 App_Code 中的服务代码。相反,您的 .ASMX 文件将根据名称引用共享服务类。并且,在调用 SOAPReceivers.Add 时,自定义宿主可以提供相同的服务类型。
问:我的小组开发了一个常用的 XSD 序列化类库,我们希望所有开发人员在实现或使用我们的服务时都使用这个类库。当他们运行“XSD.exe /c”或“WSDL.exe”时,代码生成器应该使用我们的类型,而不是生成新的类型。这可以实现吗?
答:可以。通过实现所谓的架构导入程序扩展(.NET Framework 2.0 提供的一个新扩展点)即可做到。下面讨论一下它的要点。您编写了一个从 SchemaImporterExtension 派生的类,并重写了 ImportSchemaType。在该方法的实现中,您定义了传入的 XSD 类型如何映射到现有的 .NET 类型。图 6 显示一个示例。
然后,将架构导入程序扩展配置为由 XSD.exe 或 WSDL.exe 使用。您可以在 配置部分的 元素中进行配置。或者,您可以在运行 XSD.exe 或 WSDL.exe(通过 /parameters 选项)时,通过提供要使用的架构导入程序扩展列表来完成。
问:能告诉我 /parameters 在 XSD.exe 和 WSDL.exe 中的文件格式吗?
答:XSD.exe 和 WSDL.exe 引入了一个新开关 /parameters,用于打包您要在单个 XML 文件中使用的所有命令行选项。XML 文件的格式由本专栏的下载中提供的 XML 架构定义来定义。该文件必须包含顶级 元素,该元素将包含所有选项。图 7 显示一个示例。
然后,您应该将该文件传递给 WSDL.exe,如下所示:
C:/demos> WSDL.exe /parameters:options.XML HTTP://localhost/stocks/quoteservice.ASMX
每次使用其中的一个代码生成工具时,该方法有助于确保一组一致的选项。有趣的是,某些 XSD.exe 和 WSDL.exe 功能只能通过该机制访问。
问:我听说 3.0 版可能是最后一个 WSE 版本。这是真的吗?
答:在 WCF 发布以后,WSE 很可能会停止发展新功能,从而转向支持模式(修补程序、service pack 等)。WCF 定位为新的 .NET Web 服务平台,该平台可以完全替代现在对 WSE 所提供功能的需要。并且,由于 WCF 已经临近它的发布日期,因此 WSE 3.0 很可能是最后一个发布版本。WSE 开发小组已经重组到 WCF 小组,以巩固该小组内开发人员的实力。
但是,很可能会使用类似于 WSE 的工具扩展和“增强”WCF,虽然没人知道它们将被称为什么。此类工具可用于合并新的协议和行为,因为它们能够使用各种 WCF 扩展点。
请参阅以下文档,以获得有关 WSE 的更多信息:“Developing .NET Web Services with Beta 2”、“What's New in WSE 3.0”以及 Web Services 页。