原帖地址:http://www.ibm.com/developerworks/cn/webservices/ws-address.html

Doug Davis, 架构师, IBM

2004 年 4 月 01 日

Web 服务寻址(WS-Addressing)协议可能与第一眼看上去的的样子有很大的不同。但是它构建的消息信息头将使新的 Web 服务消息流模式成为可能——而且它将对 SOAP 引擎以及 SOAP 协议本身的未来造成深远的影响。

大约在一年以前,IBM、BEA 和 Microsoft 推出了一个称为 Web 服务寻址(WS-Addressing)的新规范(请参阅下面参考资料部 分的相关链接)。在整个 14 页里,它仅仅声明了帮助人们如何标准化指定 Web 服务的位置的方法。然而,Web 服务寻址(WS-Addressing)具有从根本上改变 SOAP 处理模型的潜力。但是,究竟是怎样改变的呢?而且,更重要的是,为什么要改变呢?为了更好地回答这些问题,让我们先来研究一下这个规范本身究竟说了些什 么。(应该注意的是,这里的讨论将并不探讨这个规范的实际实现的细节;相反,我将讨论为什么它在 SOAP 中的角色将来会那么关键。)

如果您正确地了解了事实,那么在所有多余的话之后,Web 服务寻址(WS-Addressing)实际上只包括两个新概念:端点引用(endpoint reference,EPR)和 SOAP 结构的消息信息(message information,MI)头。

端点引用(Endpoint reference)

在 Web 服务寻址(WS-Addressing)出现以前,为了找到一个 Web 服务的位置,您不得不请求端点的 URL 或求助于 Web 服务描述语言(WSDL)。这听起来好像够简单了,那么您为什么还需要 Web 服务寻址(WS-Addressing)呢?随着 Web 服务的发展,很自然,它们将开始变得复杂,以便支持企业级的解决方案。例如,可能需要在 SOAP 请求中包含附加信息来帮助惟一地识别您正在与之对话的 Web 服务实例。这将与在请求中包含会话标识符或实例 ID 相类似。这么做并不是非常的重要。

然而,使用会话标识符(通过将某些像 resourceID=123 这样的信息附加到 URL)并不完全类似于 SOAP。SOAP 纯粹主义者可能更愿意回到 SOAP 中定义的可扩展性模型,并使用附加的 SOAP 头包含这个新的信息来处理消息;例如:

<widget:resourceID>123</widget:resourceID>

从技术上来说,两种方法中的任何一种 ——改变请求的 URL 或在 SOAP 头中包含识别信息——都可以很好地满足这些目的。然而,如果您使用 SOAP 头而不是在 URL 中编码这个附加信息,那么您将不再绑定于一个特定的传输协议。想象这样一种情况,一个 SOAP 消息为了到达它的最终目的地实际上通过了多个传输协议。如果两个传输协议都是 HTTP,那么就可以很容易地重用 URL 的查询字符串部分。但是其他的传输协议怎么办呢,比如 SMTP?resourceID=123 是去哪?它可能变成 SMTP 头吗?如果传输不支持用户定义的字段怎么办?或者,即使它支持,SOAP 消息的最终接收方(SOAP 引擎)将如何知道怎样向 Web 服务传送这些用户定义的字段呢?

URL 编码的另外一个问题是,现在它将特定于 Web 服务的数据紧密地绑定到传输——换句话说,现在特定于 Web 服务的数据在 SOAP 信封以外,处于传输层。

那么,Web 服务寻址(WS-Addressing)有什么用处呢?它定义了称为端点引用(endpoint reference,EPR)的东西。在它最简单的形式中,EPR 只不过就是由一些 XML 元素包装的 URL。清单 1 给出了一个示例。

清单 1. 一个简单的端点引用(EPR)
  <widget:myLocation>
<wsa:Address>http://localhost/WidgetService</wsa:Address>
</widget:myLocation>

这里,widget:myLocation 是端点引用(EPR),在它的内部是一个 Web 服务寻址的(WS-Addressing-defined)定义的元素,称为 wsa:Address (当试图与 WidgetService 对话时,使用它来指定 URL)。

清单 2 添加了附加资源 ID 信息。

清单 2. 带有添加的 ReferenceProperties 元素的端点引用(EPR)
  <widget:myLocation>
<wsa:Address>http://localhost/WidgetService</wsa:Address>
<wsa:ReferenceProperties>
<widget:resourceID>123</widget:resourceID>
</wsa:ReferenceProperties>
</widget:myLocation>

这够容易了:您可以添加一个称为 ReferenceProperties 的新 XML 元素,并且在它的内部有一个包含特定于您的 Web 服务的数据的元素,称为 resourceID (也称为引用特性(reference property))。这个 ReferenceProperties 元素可以封装所有特定于 Web 服务的数据。但是现在您将如何处理这个 XML 块呢?因为您正在使用 resourceID,那就继续使用它,并且假定您之所以使用这个附加信息是因为存在由 WidgetService 使用的多个 Widget 实例。您可以设想,在某种程度上,您或者创建了一个实例,或者请求服务返回一个指向实例的指针。假定您请求了通过调用某个 createWidget() 操作来创建的一个新的实例。但是,WidgetService 是怎样返回到这个新的 widget 的引用的呢?正如您可能已经猜到的,它是通过返回一个端点引用(EPR)来实现的,如清单 3 所示。

清单 3. 使用端点引用(EPR)来返回引用
  <soap:Envelope...>
<soap:Body>
<widget:createWidgetResponse...>
<widget:widgetReference>
<wsa:Address>http://host/WidgetService</wsa:Address>
<wsa:ReferenceProperty>
<widget:resourceID>123</widget:resourceID>
</wsa:ReferenceProperty>
</widget:widgetReference>
</widget:createWidgetResponse>
</soap:Body>
</soap:Envelope>

在这里,widgetReference 是一个端点引用(EPR)。Web 服务寻址(WS-Addressing)规范要求 Address 元素,但是 resourceID 是特定于 WidgetService 的。

您 可能一边看这个例子,一边纳闷,为什么您不是仅仅返回字符串“123“,而是返回整个端点引用(EPR)——为什么您需要增加额外的复杂性?实际上,有几 个原因说明了为什么额外的抽象实际上最后会使工作更容易。首先,消息的接收方不需要知道端点引用(EPR)的任何内容。如果您返回像“123”这么简单的 信息,那么接收方将不得不既要知道这是一个字符串,而且还要知道这个字符串的含义以及稍后应该如何使用这个字符串。潜在地,接收方可能被要求有区别地解释 返回值,这取决于它正在对话的应用程序的类型。通过使用端点引用(EPR),您已经使 Web 服务的引用的格式标准化,更确切地说,也使 Web 服务的实例的引用的格式标准化了。

端点引用(EPR)的另外一个重要方面是必需的元素:Address。因为端点引用(EPR)具有这个 Address 字段,所以这个字段可以包含与原始请求的 URL 完全不同的 URL。例如,createWidget() 调用的 URL 可能以 .../createWidgetService 结束,而随后服务调用的 URL 可能是 .../WidgetService。不必理会特定的值,关键是这个端点引用(EPR)的接收方不需要知道任何关于如何解释数据的事情;它仅仅需要知道 Address 字段是应该用于未来消息的 URL,以及所有的引用特性(reference properties)应该都是在那些消息中的 SOAP 头。所以,对于这个端点引用(EPR)的未来请求的 SOAP 信封将采用清单 4 中所给出的形式。

清单 4. 用于请求我们的样本端点引用(EPR)的 SOAP 信封
  <soap:Envelope...>
<soap:Header>
<wsa:To> http://host/WidgetService </wsa:To>
<widget:resourceID>123</widget:resourceID>
</soap:Header>
<soap:Body>
...
</soap:Body>
</soap:Envelope>

这个信封将被发送到 http://host/WidgetService(我很快将讨论 wsa:To 头)。现在,WidgetService 顺利处理这个 SOAP 消息所需的全部信息都可以在信封中获得,没有什么是特定于传输协议的——而且最重要的是,客户端应用程序完全不知道这个附加信息(消息的接收方需要它(比 如,引用特性)来顺利地处理消息)。一般来说,所有的客户端基础设施都需要知道如何处理端点引用(EPR)。

所 以,现在您已经将传送 Web 服务的引用的方法标准化了。标准化是一件好事,但是到目前为止,您几乎还没有看到前面声称的 Web 服务寻址(WS-Addressing)将改变 SOAP 本身所具有的优点。在下一部分中,您会对此有更好的理解,接下来我们介绍 Web 服务寻址(WS-Addressing)的第二个特征:消息信息(message information,MI)头。





回页首


消息信息头(MI header)

Web 服务寻址(WS-Addressing)规范定义了一些附加的(当然,也是标准的)SOAP 头,它们应该用于帮助传送关于消息的信息。在这一部分中,我将介绍其中比较有趣的一些。

To

To 只不过是目标 Web 服务的 URL。一般来说,这个 URL 和 HTTP 请求的 URL 是相同的,但是这并不是必需的。

清单 5. To 头
      	<wsa:To> http://host/WidgetService </wsa:To>

当使用端点引用(EPR)时,To 头应该与 <wsa:Address> 元素具有相同的值——查阅前面的部分中的例子可以获得更多的细节。

From

From 是消息发送方的端点引用(EPR)。如果消息接收方需要向发送消息的端点发送回消息,那么它应该使用这个端点引用(EPR)。请注意,同样也有一个 ReplyTo 头,它指示响应消息应该去往何处。From 头将用于这样的情况,如同那些由 WS-ReliableMessage 规范所管理的,Acknowledgement 需要被发送回发送方。清单 6 给出了一个运行的 From 头示例。

清单 6. From 头
      <wsa:From> 
<wsa:Address> http://client/myClient </wsa:Address>
</wsa:From>

ReplyTo

正如所提到的,来自于 Web 服务的任何响应都应该被发送给 ReplyTo 端点引用(EPR)。因为 FromReplyTo 可能是两个截然不同的端点引用(EPR),所以消息的发送方可能并不是想要接收响应的端点。我将在稍后讨论这里的含义。清单 7 举例说明了 ReplyTo 头的使用。

清单 7. ReplyTo 头
      <wsa:ReplyTo>
<wsa:Address> http://client/myReceiver </wsa:Address>
</wsa:ReplyTo>

FaultTo

如果消息的响应是 SOAP 错误,那么这个错误应该在 FaultTo 头中发送给端点引用(EPR)。清单 8 给出了一个示例。

清单 8. FaultTo 头
      <wsa:FaultTo>
<wsa:Address> http://client/FaultCatcher </wsa:Address>
</wsa:FaultTo>

MessageID

MessageID 只不过就是惟一地识别消息的 URI。清单 9 给出了一个示例。

清单 9. MessageID 头
	      <wsa:MessageID>uuid:098765</wsa:MessageID>

Action

清单 10 中显示的 Action 头是 SOAP HTTP Action 头在信封中的版本。

清单 10. Action 头
      <wsa:Action> http://host/widgetOp </wsa:Action>

RelatesTo

RelatesTo 常常用在响应消息中,用来指示它与先前知道的消息相关并且定义这种关系。Listing 11 给出了一个示例。

清单 11. RelatesTo 头
<wsa:RelatesTo RelationshipType="wsa:Response">
uuid:098765
</wsa:RelatesTo>

清单 11 中的 RelatesTo 头指示,这是先前知道的请求(Request)消息(它的 MessageIDuuid:098765)的响应(Response)消息。在异步消息传递场景中,这个信息头是很关键的——响应消息的接收方必须能够将它与原始请求消息相关联。RelatesTo 头提供了标准机制来达到这个目的。





回页首


使用信息头

Web 服务寻址(WS-Addressing)定义了所有这些要使用的特殊的头,但是您可能会这样自问:“谁关心这些?”最初,您事实上已经添加了一定程度的复 杂性,似乎直到现在都还没有用上。对于简单的请求/响应 HTTP 消息流,可能是这样的。然而,一旦您开始使用异步消息流或跨多个传输协议发送消息时,这些头就变得非常重要了。

以一个简单的异步消息交换(甚至是在 HTTP 之上)为例,在这里响应消息通过一个新的连接发送回去。服务器是如何知道下一个响应消息需要去往何处呢?一个显而易见的解决方案就是将返回地址(或者端点引用)作为应用程序数据的一部分(即,包含在 Body 中)。然而,如果您使用 Web 服务寻址(WS-Addressing),那么现在您就有一个标准位置用于这个数据:ReplyTo 消息信息头/端点引用(MI header/EPR)。同样地,如果您使用的是类似于 Web 服务可靠消息传递(WS-ReliableMessaging)(参阅参考资料)这样的规范,那么在这里潜在地有三个不同的响应消息(传统的 Reply、可能有的 Fault 以及 Acknowledgement),Web 服务寻址(WS-Addressing)提供了一个标准机制,所有的 Web 服务通过这个机制就可以表示这个信息。当这三个消息可能需要发送给三个不同的端点时,这就变得尤为关键。这与实际信封所采用的方式毫无差别,在实际信封上 有用于书写收信人地址、寄信人地址和粘贴邮票的标准位置。标准化是一件好事。





回页首


匿名 URI

在所有的信息头(或者包含 Address 元素,或者像 To 头那样对自己进行寻址)中,您可以使用一个特殊的匿名 URI:

http://schemas.xmlsoap.org/ws/2003/03/addressing/role/anonymous

当您使用这个 URI 时,您就暗示,对于这个地址并没有可用的真正端点。这就意味着不能打开到那个端点的连接。实际上,这对消息流有一些非常有趣的副作用。例如,让我们假设您在 ReplyTo 头的地址中使用了匿名 URI。这是什么意思呢?明显地,客户端不想打开一个新的连接——它不是一个真正的 URI。使用匿名 URI 就相当于根本不使用 ReplyTo 头——换句话说,响应必须在 HTTP 响应消息中流回。

虽然最初您可能觉得这个例子只是比较有趣地使用 ReplyTo 和匿名 URI,而实际上,它对于消息处理模型具有非常大的影响。考虑这样一种情况,它的请求消息类似于清单 12 所示。

清单 12. 使用匿名 URI
  <soap:Envelope...>
<soap:Header>
<wsa:To> http://host/WidgetService </wsa:To>
<wsa:ReplyTo>
<wsa:Address>
http://schemas.xmlsoap.org/ws/2003/03/addressing/role/anonymous
</wsa:Address>
</wsa:ReplyTo>
<wsa:FaultTo>
<wsa:Address>
http://client/myReceiver
</wsa:Address>
</wsa:FaultTo>
...
</soap:Header>
<soap:Body>
...
</soap:Body>
</soap:Envelope>

注意,这个消息的发送方要求通过 HTTP 响应流发送回响应消息(正如在 ReplyTo 头中匿名 URI 所指示的)。这意味着发送这个代码的客户端假定同步处理模型,是这样吗?有可能。看一下 FaultTo 头——那是一个真正的、非匿名的端点。所以,如果这个消息的接收方产生一个 SOAP 错误,那么应该将那个错误发送到 http://client/myReceiver, 而且不通过 HTTP 响应流——那是一个异步处理模型。考虑它真正的意思是什么:在某些情况中,客户端不能控制消息的传回是同步地,还是异步地。这可能会对 SOAP 引擎造成非常大的影响。它们不再能够假定每次调用一个处理模型——它们必须能够基于从服务器取回的信息来在两者之间转换。所以,在这种情况下,客户端可能 等待 HTTP 响应流上的响应。如果没有得到,那么它将等待异步到达 http://client/myReceiver 端点上的响应。这个动态的转换可能对于当前某些 SOAP 处理器来说并不是一个简单的任务。

当然,并不只是客户端受到了影响。接收方(或服务器)必须能够基于各种信息头中的值和生成的响应消息来动态地在同步模型和异步模型之间转换。

它还有另外一个有趣的副作用。在大部分服务中,WSDL 描述的是非常简单的请求/响应同步消息模式。但是对于 Web 服务寻址(WS-Addressing)来说,任何 Web 服务(无论是异步地还是同步地)都将简单地通过正确地使用 Web 服务寻址(WS-Addressing)头来隐式地支持异步。这是 SOAP 应用程序现在可用的一个非常强大的工具。某些规范,比如 WS-Transactions (WS-Coordination 和 WS-AtomicTransaction——请查阅参考资料),特意定义了两个不同的端口类型——一个用于同步消息传递模式,一个用于异步消息传递模式。这种差别现在已经变得过时了。

新兴技术工具包(Emerging Technologies Toolkit,ETTK;请参阅 参考资料以 获得相关链接)包括使用 Web 服务寻址(WS-Addressing)规范的相当多的演示。特别是,基于 Web 的 WS-ReliableMessaging demo 让您可以容易地改变 Web 服务寻址(WS-Addressing)头来查看消息流是如何改变的。





回页首


结束语

支 持 WS-* 规范最一般的原因是由于它带给 Web 服务世界的标准化程度。Web 服务寻址(WS-Addressing)通过定义端点引用(EPR)和消息信息(MI)头来明确地达到这个目的。这个定义提供了单一的机制,通过这一机 制,人们能够指定 Web 服务(或者服务的实例)的位置以及服务在 SOAP 消息中使用那些端点引用(EPR)的方式(通过 MI 头)。但是,正是在消息处理模型/流中的隐式改变甚至对 SOAP 具有更加重大的影响。Web 服务寻址(WS-Addressing)的重要性将随着时间而增加——以致到达它将被视为那些规范中的一个成员,它本身应该是核心 SOAP 规范的一部分。



参考资料



关于作者


Doug Davis 是 IBM Software Standards 部门的架构师。他以前担任过的职务包括 Emerging Technologies Toolkit、WebSphere Machine Translation、Team Connection 和 IBM Fortran 90 的技术总监。