分享

理解 SOAP

 aojilee 2006-03-07

理解 SOAP

发布日期: 4/1/2004 | 更新日期: 4/1/2004

Aaron Skonnard

DevelopMentor

2003 年 3 月

适用于:

全局 XML Web 服务结构 (GXA)

远程过程调用 (RPC)

SOAP 1.1 和 SOAP 1.2 规范

传输协议: TCP、HTTP、SMTP 和 MSMQ

Web Services Enhancements 1.0 SP1 for Microsoft .NET

XML 架构

摘要: SOAP 提供一种简单的、可扩展并且功能丰富的 XML 消息处理框架,用于定义高级别的应用程序协议,从而在分布式异构环境中提供更高的互操作性。(20 页打印页)

*
本页内容
简介 简介
SOAP 版本 SOAP 版本
消息处理框架 消息处理框架
扩展性 扩展性
处理模型 处理模型
协议绑定 协议绑定
HTTP 绑定 HTTP 绑定
RPC 和编码 RPC 和编码
SOAP 类型 SOAP 类型
小结 小结

简介

就在不久以前,SOAP 还不过是指肥皂而已。 而如今,大多数开发人员一听到这个词眼前就会浮现出一些尖括号来。 SOAP 最初代表“简单对象访问协议”。 如果在几年前问任何一个人 SOAP 的含义,他们很可能这样回答:“SOAP 是用来使 DCOM 和 Corba(例如,RPC 调用)在互联网上工作”。 原作者们也承认,在那时他们注重于“访问对象”,但随着时间的推移,人们希望 SOAP 能够处理更广泛的情况。 因此,SOAP 规范的重心很快从对象转移到通用的 XML 消息处理框架上。

这种重心的变化给 SOAP 缩写词中的 "O" 带来了一点小问题。 有意思的是,SOAP 1.2 工作组沿用了(到目前为止)SOAP 这个名称(为什么不呢?这个词太流行了),但决定不再把这个词拼出来以免误导开发人员。 如今,在最新的 SOAP 1.2 规范中,其正式的定义并不提及对象:

SOAP 是一种轻量级协议,用于在分散型、分布式环境中交换结构化信息。 SOAP 利用 XML 技术定义一种可扩展的消息处理框架,它提供了一种可通过多种底层协议进行交换的消息结构。 这种框架的设计思想是要独立于任何一种特定的编程模型和其他特定实现的语义。

这个定义确实体现了 SOAP 现在的主旨。 SOAP 定义了一种方法以便将 XML 消息从 A 点传送到 B 点(参见图 1)。 为此,它提供了一种基于 XML 且具有以下特性的消息处理框架:1) 可扩展,2) 可通过多种底层网络协议使用,3) 独立于编程模型。 以下将分别详细讨论这三种特性。


图 1. 简单的 SOAP 消息处理

首先,SOAP 可扩展性是关键所在。 在这个缩写词还代表某些含义时,"S" 意味着“简单”。 如果我们从 Web 中学到了一样东西,那就是,简单性总是比效率和纯技术更重要,因而互操作性成败的关键,就在于必须绝对要求简单性。 简单性仍然是 SOAP 的主要设计目标之一,这一点的例证就是 SOAP 缺少分布式系统的很多特性(如安全性、路由和可靠性等)。 SOAP 定义了一种通信框架,允许以分层扩展的形式随着时间推移而加入这些特性。 Microsoft、IBM 和其他软件厂商正在积极开发一个 SOAP 扩展的通用套件,该套件将加入大多数开发人员期待的特性。 这一计划被称为全局 XML Web 服务结构 (GXA)Microsoft 已经发布了针对若干 GXA 规范的一个参考实现,并将其命名为 Web Services Enhancements 1.0 SP1 for Microsoft .NET (WSE)

其次,SOAP 可在任何传输协议(诸如 TCP、HTTP、SMTP,甚至是 MSMQ)上使用(参见图 1)。 然而,为了保持互操作性,需要定义一些标准协议绑定以便草拟用于每种环境的规则。 SOAP 规范提供了一种用于定义任意协议绑定的灵活框架,并且由于 HTTP 的使用极为广泛,它现已为 HTTP 提供了一种显式绑定。

第三,SOAP 允许任何编程模型,并且不依赖于 RPC。 大多数开发人员立刻将 SOAP 与对分布式对象进行的 RPC 调用等效起来(因为 SOAP 最初就是关于“访问对象”的),但实际上,基本的 SOAP 模型更接近于传统的消息处理系统,如 MSMQ。 SOAP 定义了一种模型以便处理个别的单向消息。 你可以将多条消息组合成一条整体的消息交换。 图 1 说明了一种简单的单向消息,其中发送方不会收到响应。 但是,接收方可以向发送方发回一条响应(参见图 2)。 SOAP 允许使用任何数量的消息交换模式 (MEP),请求/响应只是其中一种。 其他示例包括要求/响应(与请求/响应相对)、通知和长期运行的点对点对话等。


图 2. 请求/响应消息交换模式

开发人员经常将请求/响应与 RPC 混为一谈,而实际上二者之间的差别很大。 RPC 使用请求/响应,但请求/响应不一定就是 RPC。 RPC 是 一种允许开发人员进行方法调用的编程模型。 RPC 需要将方法签名转换成 SOAP 消息。 鉴于 RPC 的广泛应用,SOAP 草拟了一项协议,以便将 RPC 用于 SOAP(参见本文稍后的RPC 和编码一节)。

具备这三种主要特性,SOAP 消息处理框架就促进了在异构环境中交换 XML 消息,而在这类环境中,互操作性长久以来都是极大的挑战。

SOAP 版本

从第一个发布的 SOAP 规范到如今被广泛实施的 SOAP 1.1,很多方面都发生了改变,从琐碎的细节到思想的重大转变。 SOAP 1.1 被提交给 W3C,并于 2000 年 5 月被发布为 Note。由于 SOAP 1.1 未能通过 W3C 过程的严格审核,"W3C Note" 状态使其还停留在仅是一个好主意的层次,但当完成时,它将最终达到“推荐”状态。 然而,由于如今 SOAP 1.1 得到了大小厂商如此广泛的支持,它仍然被认为是事实上的标准。

W3C 使用 SOAP 1.1 Note 作为新 XML 协议工作组的基础,负责产生下一版本的 SOAP,目前命名为SOAP 1.2。 SOAP 1.2 当前是一种“候选推荐方案”,意味着它正处在实施阶段且离最后完成为期不远。 一旦 SOAP 1.2 成为“推荐方案”,它极有可能很快获得厂商的支持。

在 SOAP 1.2 发布之后,为了提供向后兼容性,厂商应该继续支持 SOAP 1.1。 SOAP 版本控制基于 XML 命名空间。 SOAP 1.1 由 http://schemas./soap/envelope/ 命名空间标识,而 SOAP 1.2 由 http://www./2002/12/soap-envelope 命名空间标识(尽管当其成为推荐方案时,这也将改变)。

有关每个版本的命名空间名称和规范所在位置,请参见表 1。 在本文的剩余部分中,我们将讲述 SOAP 1.1 最重要的一些方面。 要了解两个版本之间完整的更改列表,请查看当前的 SOAP 1.2 规范。

表 1. SOAP 版本信息

SOAP 1.1

命名空间名称

规范位置

SOAP 1.2

命名空间名称

规范位置

http://schemas./soap/envelope/

http://www./TR/SOAP/

http://www./2002/12/soap-envelope/

http://www./TR/soap12-part0/(入门)

http://www./TR/soap12-part1/

http://www./TR/soap12-part2/

消息处理框架

SOAP 规范的核心部分就是消息处理框架。 SOAP 消息处理框架定义了一整套 XML 元素,用以“封装”任意 XML 消息以便在系统之间传输。

该框架包括以下核心 XML 元素: Envelope、Header、Body 和 Fault,所有这些都来自 SOAP 1.1 中的 http://schemas./soap/envelope/ 命名空间。 以下代码中提供了 SOAP 1.1 的完整 XML 架构定义,以供在阅读下文时参考。 我个人认为,每次让自己熟悉各种 XML 结构时,检查一下该架构是颇有帮助的。

SOAP 1.1 XML 架构定义

<xs:schema xmlns:xs="http://www./2001/XMLSchema"
xmlns:tns="http://schemas./soap/envelope/"
targetNamespace="http://schemas./soap/envelope/"
>
<!-- Envelope, header and body -->
<xs:element name="Envelope" type="tns:Envelope" />
<xs:complexType name="Envelope" >
<xs:sequence>
<xs:element ref="tns:Header" minOccurs="0" />
<xs:element ref="tns:Body" minOccurs="1" />
<xs:any namespace="##other" minOccurs="0"
maxOccurs="unbounded" processContents="lax" />
</xs:sequence>
<xs:anyAttribute namespace="##other"
processContents="lax" />
</xs:complexType>
<xs:element name="Header" type="tns:Header" />
<xs:complexType name="Header" >
<xs:sequence>
<xs:any namespace="##other" minOccurs="0"
maxOccurs="unbounded" processContents="lax" />
</xs:sequence>
<xs:anyAttribute namespace="##other"
processContents="lax" />
</xs:complexType>
<xs:element name="Body" type="tns:Body" />
<xs:complexType name="Body" >
<xs:sequence>
<xs:any namespace="##any" minOccurs="0"
maxOccurs="unbounded" processContents="lax" />
</xs:sequence>
<xs:anyAttribute namespace="##any"
processContents="lax" />
</xs:complexType>
<!-- Global Attributes -->
<xs:attribute name="mustUnderstand" default="0" >
<xs:simpleType>
<xs:restriction base=‘xs:boolean‘>
<xs:pattern value=‘0|1‘ />
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="actor" type="xs:anyURI" />
<xs:simpleType name="encodingStyle" >
<xs:list itemType="xs:anyURI" />
</xs:simpleType>
<xs:attribute name="encodingStyle"
type="tns:encodingStyle" />
<xs:attributeGroup name="encodingStyle" >
<xs:attribute ref="tns:encodingStyle" />
</xs:attributeGroup>
<xs:element name="Fault" type="tns:Fault" />
<xs:complexType name="Fault" final="extension" >
<xs:sequence>
<xs:element name="faultcode" type="xs:QName" />
<xs:element name="faultstring" type="xs:string" />
<xs:element name="faultactor" type="xs:anyURI"
minOccurs="0" />
<xs:element name="detail" type="tns:detail"
minOccurs="0" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="detail">
<xs:sequence>
<xs:any namespace="##any" minOccurs="0"
maxOccurs="unbounded" processContents="lax" />
</xs:sequence>
<xs:anyAttribute namespace="##any"
processContents="lax" />
</xs:complexType>
</xs:schema>

如果检查一下 EnvelopecomplexType 定义,你很快就能了解这些元素相互之间是如何关联的。 以下消息模板说明了 SOAP Envelope 的结构:

<soap:Envelope
xmlns:soap="http://schemas./soap/envelope/">
<soap:Header> <!-- optional -->
<!-- header blocks go here... -->
</soap:Header>
<soap:Body>
<!-- payload or Fault element goes here... -->
</soap:Body>
</soap:Envelope>

Envelope 元素始终是 SOAP 消息的根元素。 这就便于应用程序识别“SOAP 消息” — 只要检查一下根元素的名称即可。 通过检查 Envelope 元素的命名空间,应用程序也可确定所使用的 SOAP 版本。

Envelope 元素包含一个可选的 Header 元素(有关详细信息,参见可扩展性一节),后跟一个必要的 Body 元素。 Body 元素代表了该消息的有效内容。 它是一种通用容器,因为它可包含来自任何命名空间的任意数量的元素。 这就是试图发送数据的最终目的地。

例如,以下的 SOAP 消息代表了一个在银行帐户之间转帐的请求:

<soap:Envelope
xmlns:soap="http://schemas./soap/envelope/">
<soap:Body>
<x:TransferFunds xmlns:x="urn:examples-org:banking">
<from>22-342439</from>
<to>98-283843</to>
<amount>100.00</amount>
</x:TransferFunds>
</soap:Body>
</soap:Envelope>

如果接收方支持请求/响应,且能够成功地处理该消息,它应向最初的发送方返回另一条 SOAP 消息。 在这种情况下,响应信息也应包含在 Body 元素中,如下例所示:

<soap:Envelope
xmlns:soap="http://schemas./soap/envelope/">
<soap:Body>
<x:TransferFundsResponse
xmlns:x="urn:examples-org:banking">
<balances>
<account>
<id>22-342439</id>
<balance>33.45</balance>
</account>
<account>
<id>98-283843</id>
<balance>932.73</balance>
</account>
</balances>
</x:TransferFundsResponse>
</soap:Body>
</soap:Envelope>

该消息处理框架还定义了一个名为Fault 的元素,用于在发生错误时在 Body 元素中表示错误。 这是不可缺少的,因为如果没有一种标准的错误表示方法,每个应用程序将不得不自己创建,从而使得通用基础结构不可能区分成功和失败。 以下示例 SOAP 消息中包含了一个 Fault 元素,指明在处理该请求时发生了“Insufficient Funds(资金不足)”错误:

<soap:Envelope
xmlns:soap="http://schemas./soap/envelope/">
<soap:Body>
<soap:Fault>
<faultcode>soap:Server</faultcode>
<faultstring>Insufficient funds</faultstring>
<detail>
<x:TransferError xmlns:x="urn:examples-org:banking">
<sourceAccount>22-342439</sourceAccount>
<transferAmount>100.00</transferAmount>
<currentBalance>89.23</currentBalance>
</x:TransferError>
</detail>
</x:TransferFunds>
</soap:Body>
</soap:Envelope>

Fault 元素必须包含一个 faultcode,后跟一个 faultstring 元素。 faultcode 元素使用一种符合命名空间的名称对错误进行分类,而 faultstring 元素提供一种对错误可读的解释(类似于 HTTP 的工作方式)。 表 2 简要地说明了 SOAP 1.1 所定义的各种错误码(所有这些代码都包含在 http://schemas./soap/envelope/ 命名空间中)。

Fault 元素也可能包含一个 detail 元素,以便提供该错误的细节,这样可以帮助客户端诊断问题,特别是在 Client 和 Server 错误码的情况下。

表 2. SOAP 1.1 错误码

名称

VersionMismatch

MustUnderstand

Client

Server

含义

处理方发现 SOAP Envelope 元素的命名空间是无效的。

处理方没有理解或服从 SOAP Header 元素的某个直接子元素,而该子元素包含一个值为 "1" 的 SOAP mustUnderstand 属性。

Client 类的错误表明消息的格式错误或者不包含适当的信息,因而不能成功。 这通常表明,如果不对该消息做出更改,就不应该重发该消息。

Server 类的错误表明该消息未能得到处理的原因与消息的内容并没有直接关系,而是跟该消息的处理有关。 例如,处理过程可能包括与某个上游处理器的通信,但该处理器没有响应。 如果在稍后重发,该消息可能会成功。

现在,假设你想在初始的消息中增加一些验证信息,以便接收方能够确定发送方是否有足够的权限来执行传输。 要达到这一目的,一种方法就是在主体中添加凭证信息,如下所示:

<soap:Envelope
xmlns:soap="http://schemas./soap/envelope/">
<soap:Body>
<x:TransferFunds xmlns:x="urn:examples-org:banking">
<from>22-342439</from>
<to>98-283843</to>
<amount>100.00</amount>
<!-- security credentials -->
<credentials>
<username>dave</username>
<password>evad</password>
</credentials>
</x:TransferFunds>
</soap:Body>
</soap:Envelope>

如果使用这种方法,每项需要验证的操作都必须处理这些凭证。 这也意味着其他需要安全性的应用程序必须开发自己的解决方案以解决这个问题;归根结底,这将损害互操作性。 对于诸如安全性等公共需要,定义各方都同意的标准 SOAP 标头将更有意义。 然后,各厂商可以在其通用的 SOAP 基础结构中建立对扩展功能的支持,这样各方皆赢。 这种方法可提高开发人员的生产力,同时有助于确保更高级别的互操作性。 而这正是 SOAP 扩展性模型设计要实现的目标。

扩展性

大多数现有的协议都区分控制信息(例如,标头)和消息有效负载。 在这方面,SOAP 也不例外。 SOAP Header 和 Body 元素在易于处理的 XML 世界中也进行同样的区分。 除了易用性之外,可扩展 Envelope 的关键优势在于它可用于任何通讯协议。

在各种应用程序协议中(如 HTTP、SMTP 等)标头总是具有重要的意义,因为标头允许连网两端的应用程序就所支持命令的具体行为进行协商。 尽管 SOAP 规范本身并不定义任何内置的标头,标头将逐渐在 SOAP 中扮演同等重要的角色。 随着 GXA 日趋成熟及 SOAP 标头的标准化,开发人员能够更方便地定义丰富的应用程序协议,而不必每次都重新开始。

与 Body 元素类似,Header 元素是控制信息的通用容器。 其中可包含来自任何命名空间(除 SOAP 命名空间之外)的任意数量的元素。 放置在 Header 元素中的各个元素被称为标头块。 如同其他协议一样,标头块中包含的信息应该能够影响有效负载的处理。 因此,这里正适于放置诸如凭证一类的元素,以帮助控制对操作的访问:

<soap:Envelope
xmlns:soap="http://schemas./soap/envelope/">
<soap:Header>
<!-- security credentials -->
<s:credentials xmlns:s="urn:examples-org:security">
<username>dave</username>
<password>evad</password>
</s:credentials>
</soap:Header>
<soap:Body>
<x:TransferFunds xmlns:x="urn:examples-org:banking">
<from>22-342439</from>
<to>98-283843</to>
<amount>100.00</amount>
</x:TransferFunds>
</soap:Body>
</soap:Envelope>

我们也可以利用一个名为 mustUnderstand 的全局 SOAP 属性对标头块进行标注,以指明接收方在处理该消息之前是否需要理解标头。 以下示例说明了如何要求对凭证标头进行处理:

<soap:Envelope
xmlns:soap="http://schemas./soap/envelope/">
<soap:Header>
<!-- security credentials -->
<s:credentials xmlns:s="urn:examples-org:security"
soap:mustUnderstand="1"
>
<username>dave</username>
<password>evad</password>
</s:credentials>
</soap:Header>
...

如果某个标头块被标注为 mustUnderstand="1",而接收方未设计为支持给定的标头,则不应处理该消息,而应该向发送方返回一条 Fault (带有 soap:MustUnderstand 状态码)。 如果 mustUnderstand="0" 或者没有提供 mustUnderstand 属性,则接收方可以忽略相应的标头块并继续进行处理。 在整个 SOAP 处理模块中,mustUnderstand 属性起着核心作用。

处理模型

SOAP 定义了一种处理模型,它大致规定了从 SOAP 发送方传输到 SOAP 接收方的过程中对 SOAP 消息的处理规则。 图 1说明了最简单的 SOAP 消息处理方案,其中一个应用程序(SOAP 发送方)向另一个应用程序(SOAP 接收方)发送一条 SOAP 消息。

但是,处理模型允许使用一些更有趣的结构(如图 3 中的结构),这类结构中包含多个中间 节点。 在下文中,将使用 SOAP 节点 这个术语指代任何要处理 SOAP 消息的应用程序,不管是最初的发送方、中间节点还是最终的接收方;否则,我将明确指出并使用相应的准确术语。


图 3. 高级 SOAP 消息处理

中间节点位于最初的发送方和最终的接收方之间,并截获 SOAP 消息。 中间节点可同时作为 SOAP 发送方和 SOAP 接收方。 中间节点使得有可能设计一些有趣且灵活的网络体系结构,而这些网络结构能受到的消息内容影响。 SOAP 路由就是一个很好的示例,它很大程度上利用了 SOAP 中间节点(有关 SOAP 路由的详细信息,请查看 Routing SOAP Messages with Web Services Enhancements 1.0)。

在处理消息时,SOAP 节点承担一个或者多个角色 (role),这些角色会影响如何处理 SOAP 标头。 各个角色被赋予独特的名称(以 URI 的形式),以便在处理过程中能够识别这些角色。 当 SOAP 节点接收到一条要处理的消息时,它首先必须确定要假定哪些角色。 它可以检查该 SOAP 消息以帮助确定。

一旦 SOAP 节点确定了要扮演的角色,它随后必须处理针对其角色之一的所有必要标头(标记为mustUnderstand="1" )。 SOAP 节点也可选择处理针对其角色之一的任何可选标头(标记为 mustUnderstand="0")。

SOAP 1.1 只定义了一个名为 http://schemas./soap/actor/next 的角色(简写为 next)。 每个 SOAP 节点都必须承担 next 角色。 因此,当 SOAP 消息到达任一给定的 SOAP 节点时,该节点必须处理针对 next 角色的所有必要标头,它可以选择处理针对该 next 角色的可选标头。 除 next 外,SOAP 1.2 定义了另外一些角色(参见表 3),且应用程序也可以定义自定义角色。

SOAP 标头通过全局 actor 属性(在 SOAP 1.2 中该属性名为 role )来指定具体的角色。 如果不存在 actor 属性,则标头默认地指向最终的接收方。 以下 SOAP 消息说明了如何使用 actor:

<soap:Envelope
xmlns:soap="http://schemas./soap/envelope/">
<soap:Header>
<wsrp:path xmlns:wsrp="http://schemas./rp"
soap:actor="http://schemas./soap/actor/next"
soap:mustUnderstand="1"
>
...

由于 wsrp:path 标头被指定为 next 角色且被标记为必要的 (mustUnderstand="1"),因此接收到此消息的第一个 SOAP 节点必须根据该标头块的规范来处理此消息,在这种情况下为 WS-Routing。 如果 SOAP 节点不理解针对其角色之一的某个必要的标头,则它必须产生一个带 soap:MustUnderstand 状态码的 SOAP 错误,并停止处理。 SOAP Fault 元素提供了faultactor 子元素,以指定在消息路径中哪个节点导致了该错误的发生。faultactor 属性的值是一个 URI,用以标识导致该错误的 SOAP 节点。

如果 SOAP 节点成功地处理了一个标头,则它必须从消息中删除该标头。 SOAP 节点可以再插入标头,但是这样做会改变合同方 — 它现在处于当前节点与该标头所指向的下一节点之间。 如果 SOAP 节点恰好是最终的接收方,则它还必须处理 SOAP 主体。

表 3. SOAP 1.2 角色

SOAP 角色名称

http://www./2002/06/soap-envelope/role/next

http://www./2002/06/soap-envelope/role/none

http://www./2002/06/soap-envelope/role/ultimateReceiver

说明

每个 SOAP 中间节点和最终的 SOAP 接收方必须 (MUST) 扮演此角色,并可以 (MAY) 另外承担零个或多个其他 SOAP 角色。

SOAP 节点绝不可以 (MUST NOT) 扮演此角色。

要将某个 SOAP 节点确立为最终的接收方,该 SOAP 节点必须 (MUST) 扮演此角色。 SOAP 中间节点绝不能 (MUST NOT) 扮演此角色。

协议绑定

图 3中一个有趣之处是 SOAP 允许通过多种底层协议进行消息交换。 由于 SOAP 消息处理框架独立于底层协议,每个中间节点可以选择使用不同的通信协议而不会影响 SOAP 消息。 然而,为了确保各种 SOAP 应用程序和基础结构之间高级别的互操作性,标准的协议绑定是必要的。

一种具体的协议绑定准确地定义了应该如何利用给定的协议来传输 SOAP 消息。 换言之,它详细定义了 SOAP 如何适用于另一协议的范围,该协议很可能具有自己的消息处理框架以及多种标头。 协议绑定实际所定义的内容很大程度上取决于该协议的功能和选项。 例如,针对 TCP 的协议绑定应很大程度不同于针对 MSMQ 或针对 SMTP 的协议绑定。

SOAP 1.1 规范仅规范化了一种用于 HTTP 的协议绑定(由于 HTTP 的广泛使用)。 SOAP 已经用于 HTTP 之外的很多协议,但是其实现并未遵循标准化的绑定。 当你尝试与利用相同协议的其他 SOAP 实施进行集成时,只要准备好处理各种互操作性方面的问题,超前一点而不使用标准的协议绑定也未尝不可。

HTTP 绑定

HTTP 协议绑定定义了在 HTTP 上使用 SOAP 的规则。 SOAP 请求/响应自然地映射到 HTTP 请求/协议模型。 图 4 说明了 SOAP HTTP 绑定的很多细节。


图 4. SOAP HTTP 绑定

HTTP 请求和响应消息的 Content-Type 标头都必须设为 text/xml (在 SOAP 1.2 中是 application/soap+xml)。 对于请求消息,它必须使用 POST 作为动词,而 URI 应该识别 SOAP 处理器。 SOAP 规范还定义了一个名为 SOAPAction 的新 HTTP 标头,所有 SOAP HTTP 请求(即使是空的)都必须包含该标头。 SOAPAction 标头旨在表明该消息的意图。 对于 HTTP 响应,如果没有发生任何错误,它应该使用 200 状态码,如果包含 SOAP 错误,则应使用 500

RPC 和编码

尽管 SOAP 规范已日渐远离对象,它仍然定义了一种约定,以便利用上述的消息处理框架来封装并交换 RPC 调用。 定义一种标准的方法将 RPC 调用映射到 SOAP 消息,这使得在运行时基础结构可以在方法调用和 SOAP 消息之间自动转换,而不用围绕 Web 服务平台重新设计代码。

要利用 SOAP 进行方法调用,基础结构需要以下信息:

1.终结点位置 (URI)

2.方法名称

3.参数名称/值

4.可选的方法签名

5.可选的标头数据

这些信息可以通过多种方法来传输,包括类型库、IDL 文件,或者,更好的是 WSDL 文件。 SOAP RPC 绑定定义了如何在 SOAP 主体中封装并表示这些信息。 为此,RPC 绑定首先定义如何将方法签名映射到简单的请求/响应结构,然后将这些结构以 XML 进行编码。 RPC 绑定规定将以一个按照方法命名的 struct 来模拟该方法调用。 该结构将包含对应于每个 [in][in/out] 参数的一个访问器,访问器的名称与参数名相同,其次序由消息签名确定。 方法响应也将作为一个结构来建模。 结构的名称无关紧要,尽管约定是使用方法名后跟 "Response"(例如,对于 add 操作,方法响应名应该相应为 addResponse)。 响应结构包含一个用于返回值的访问器(其名称在 SOAP 1.1 中无关紧要,但在 SOAP 1.2 必须是 rpc:result),其后是针对每个 [out][in/out] 参数的访问器。

让我们来看一个示例。 假设 add 操作具有以下的 C# 方法签名:

double add(ref double x, double y)

根据刚才所说明的 RPC 绑定规则,代表该方法调用的请求结构应如下建模:

struct add {
double x;
double y;
}

而响应结构如下:

struct addResponse {
double result;
double x;
}

现在的问题是: 应该如何将这些结构映射到 XML(R) SOAP 规范定义了一组编码规则,专门用于此用途。 SOAP 编码规则大致阐述了如何将当今最常用的数据结构(如结构和数组)映射到普通的 XML 格式。 根据 SOAP 编码规则,以上的请求结构应映射到以下 XML 消息(这将放在 SOAP 主体中):

<add>
<x>33</x>
<y>44</y>
</add>

且上述请求的响应消息将映射到以下 XML 消息(这个消息将进入响应消息的主体):

<addResponse>
<result>77</result>
<x>33</x>
</addResponse>

XML 架构的相关工作刚刚开始,SOAP 编码规则即已创立。 既然 XML 架构已经完成,开发人员可以简单地提供文字的 XML 架构定义,从而准确指定应该如何以 XML 来格式化请求/响应消息。 由于利用 XML 架构定义更易于获得互操作性,因此大多数开发人员已经决定完全摒弃 SOAP 编码规则。 实际上,自 SOAP 1.2 起,SOAP 规范不再正式要求支持 SOAP 编码规则。 从现在起,最好避免使用 SOAP 编码规则,有关此中原由的全面讨论,请查看关于 SOAP 编码的讨论一文。

尽管 SOAP RPC 绑定和编码规则为那些不愿意涉及诸如 XML 架构和 WSDL 等的应用程序提供了一个很好的 SOAP 集成层,因为 RPC 绑定和编码规则易于导致互操作性方面的问题,它们基本上已经失宠于 Web 服务社区。

SOAP 类型

要重申的是,如今有两种基本类型的 SOAP 消息处理: 文档和 RPC。 文档类型指出主体只是包含一个 XML 文档,而发送方和接收方都必须遵循该文档的格式。 另一方面,RPC 类型指出主体中包含某个方法调用的 XML 表示,正如刚才所述。

两种方法可用于确定如何将数据序列化到主体中: 使用文字的 XML 架构定义和使用 SOAP 编码规则。 利用前一种方法,架构定义逐字确定了主体的 XML 格式,不具有二义性。 然而,利用后一种方法,SOAP 处理器必须在运行时遍历各种 SOAP 编码规则以确定主体正确的序列化。 很显然,这种方法更易于导致错误和互操作性方面的问题。

最常见的情形是在使用文档类型时也使用文字架构定义(称为文档/文字),以及在使用 SOAP 编码规则时使用 RPC 类型(称为 rpc/编码)。 文档/编码和 rpc/文字也是可能的,但并不常见,也没有太大意义。 大多数 Web 服务平台都集中于文档/文字类型,将其作为发展的主要用例,且是现今 Microsoft ASP.NET WebMethod 框架的默认设置。

小结

SOAP 定义了一种简单而可扩展的 XML 消息处理框架,它可以通过多种协议用于各种不同的编程模型,尽管此规范中规范化了如何将 SOAP 用于 HTTP 和 RPC 调用。 SOAP 还定义了一个完整的处理模型,大致规定了当消息沿路径传送时如何对其进行处理。 总的来说,SOAP 提供了一个功能丰富而灵活的框架以便定义高级应用程序协议,这些协议可在分布式异构环境中提供更好的互操作性。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多