了解 SOAP

 

Aaron Skonnard
DevelopMentor

2003 年 3 月

适用于:
   全局 XML Web Services 体系结构 (GXA)
   远程过程调用 (RPC)
   SOAP 1.1 和 SOAP 1.2 规范
   传输协议:TCP、HTTP、SMTP 和 MSMQ
   适用于 Microsoft® .NET 的 Web 服务增强 1.0 SP1
   XML 架构

总结: SOAP 提供简单、可扩展且丰富的 XML 消息传送框架,用于定义更高级别的应用程序协议,从而在分布式异类环境中提供更高的互操作性。 ) (20 个打印页

目录

简介
SOAP 版本
Messaging Framework
扩展性
处理模型
协议绑定
HTTP 绑定
RPC 和编码
SOAP 样式
总结

简介

好像昨天 SOAP 不仅仅是一种清洁产品。 现在,大多数开发人员在没有看到尖括号的情况下无法听到这个词。 SOAP 最初代表“简单对象访问协议”。 如果你在几年前问过任何人 SOAP 是什么意思,他们可能会说类似“这是为了让 DCOM 和 Corba (例如 RPC 呼叫) 通过 Internet 工作”。 原作者承认,他们当时专注于“访问对象”,但随着时间的推移,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 Services 体系结构 (GXA) 。 Microsoft 已发布多个 GXA 规范的参考实现,并将其称为 适用于 Microsoft .NET 的 Web 服务增强 1.0 SP1 (WSE)

其次,SOAP 可以通过 TCP、HTTP、SMTP 等任何传输协议使用,甚至 MSMQ (见图 1) 。 但是,为了保持互操作性,需要定义标准协议绑定,以概述每个环境的规则。 SOAP 规范提供了一个灵活的框架来定义任意协议绑定,并且现在为 HTTP 提供了显式绑定,因为它已得到广泛使用。

第三,SOAP 允许任何编程模型,并且不绑定到 RPC。 大多数开发人员立即将 SOAP 等同于 (分布式对象上发出 RPC 调用,因为它最初是关于“访问对象”) ,而事实上,基本 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 月作为说明发布。 “W3C 注释”状态使 SOAP 1.1 只是一个好主意,因为它没有经过 W3C 过程的严格要求,最终会在完成后达到“建议”状态。 然而,由于目前大型和小型供应商都广泛支持 SOAP 1.1,它仍然被认为是事实上的标准。

W3C 使用 SOAP 1.1 说明作为新的 XML 协议工作组的种子,该工作组负责生成下一版本的 SOAP,当前名为 SOAP 1.2。 SOAP 1.2 目前是“候选建议”,这意味着它正处于实现阶段,离完成不远了。 一旦 SOAP 1.2 成为“建议”,它可能会很快获得供应商的支持。

SOAP 1.2 发布后,供应商应继续支持 SOAP 1.1 以实现向后兼容性。 SOAP 版本控制基于 XML 命名空间。 SOAP 1.1 由 https://schemas.xmlsoap.org/soap/envelope/ 命名空间标识,而 SOAP 1.2 由 http://www.w3.org/2002/12/soap-envelope 命名空间 (标识,但当它成为建议) 时,这将会更改。

有关每个版本的命名空间名称和规范位置的快速参考,请参阅表 1。 在本文的其余部分,我们将介绍 SOAP 1.1 的最重要方面。 查看当前的 SOAP 1.2 规范,了解两个版本之间的 广泛更改列表

表 1. SOAP 版本信息

SOAP 1.1  
命名空间名称 https://schemas.xmlsoap.org/soap/envelope/
规范位置 http://www.w3.org/TR/SOAP/
SOAP 1.2  
命名空间名称 http://www.w3.org/2002/12/soap-envelope
规范位置 http://www.w3.org/TR/soap12-part0/ (入门)

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

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

Messaging Framework

SOAP 规范的核心部分是消息传递框架。 SOAP 消息传送框架定义了一套 XML 元素,用于“打包”任意 XML 消息,以便在系统之间传输。

框架由以下核心 XML 元素组成:Envelope、Header、Body 和 Fault,所有这些元素都来自 https://schemas.xmlsoap.org/soap/envelope/ SOAP 1.1 中的命名空间。 在阅读本部分的其余部分时,我在以下代码中提供了 SOAP 1.1 的完整 XML 架构定义以供参考。 就我个人而言,每当熟悉 XML 构造时,检查架构很有帮助。

SOAP 1.1 XML 架构定义

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"        
  xmlns:tns="https://schemas.xmlsoap.org/soap/envelope/"         
  targetNamespace="https://schemas.xmlsoap.org/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>

如果检查信封complexType 定义,则可以快速了解这些元素之间的关系。 以下消息模板演示了 SOAP 信封的结构:

<soap:Envelope
   xmlns:soap="https://schemas.xmlsoap.org/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 元素表示消息有效负载。 Body 元素是一个泛型容器,它可以包含任意命名空间中的任意数量的元素。 这是你尝试发送的数据的最终位置。

例如,以下 SOAP 消息表示在银行帐户之间转移资金的请求:

<soap:Envelope
 xmlns:soap="https://schemas.xmlsoap.org/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="https://schemas.xmlsoap.org/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 元素,该元素指示处理请求时出现“资金不足”错误:

<soap:Envelope
  xmlns:soap="https://schemas.xmlsoap.org/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 元素必须包含一个 错误代码 ,后跟 一个 faultstring 元素。 faultcode 元素使用命名空间限定的名称对错误进行分类,而 faultstring 元素提供错误 (类似于 HTTP) 工作方式的人工可读解释。 表 2 提供了 SOAP 1.1 定义的故障代码的简要说明, (所有这些代码都位于 https://schemas.xmlsoap.org/soap/envelope/ 命名空间) 。

Fault 元素还可能包含一个 detail 元素,用于提供有关错误的详细信息,这有助于客户端诊断问题,尤其是在客户端和服务器错误代码的情况下。

表 2. SOAP 1.1 错误代码

名称 含义
VersionMismatch 处理方发现 SOAP Envelope 元素的命名空间无效。
MustUnderstand SOAP Header 元素中未被处理方理解或不服从的直接子元素包含值为“1”的 SOAP mustUnderstand 属性。
客户端 “客户端”类错误指示消息格式不正确或不包含适当的信息以成功。 这通常表明,如果不进行更改,则不应重新发送消息。
服务器 服务器类错误指示无法处理消息的原因不是直接归因于消息的内容,而是消息的处理。 例如,处理可能包括与没有响应的上游处理器通信。 如果在以后的时间点重新发送消息,则消息可能会成功。

现在,假设你要向原始消息添加一些身份验证信息,以便接收方可以确定发送方是否具有执行传输的足够权限。 执行此操作的一种方法是将凭据信息添加到正文中,如下所示:

<soap:Envelope
 xmlns:soap="https://schemas.xmlsoap.org/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 标头和正文元素在易于处理的 XML 世界中提供相同的区别。 除了易用性外,可扩展信封的主要优点是它可以与任何通信协议一起使用。

标头在应用程序协议(如 HTTP、SMTP 等)中一直发挥着重要作用,因为它们允许网络两端的应用程序协商受支持命令的行为。 尽管 SOAP 规范本身未定义任何内置标头,但标头最终将在 SOAP 中扮演同样重要的角色。 随着 GXA 的成熟和 SOAP 标头的标准化,开发人员将更容易定义丰富的应用程序协议,而无需每次都重新发明轮子。

Header 元素(与 Body 元素一样)是控件信息的泛型容器。 它可以包含除 SOAP 命名空间) 以外的任何命名空间 (任意数量的元素。 放置在 Header 元素中的元素称为 标头块。 与其他协议一样,标头块应包含影响有效负载处理的信息。 因此,这是放置类似凭据元素之类的内容的正确位置,可帮助控制对操作的访问:

<soap:Envelope
 xmlns:soap="https://schemas.xmlsoap.org/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="https://schemas.xmlsoap.org/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” 进行批注,并且接收方未设计为支持给定标头,则不应处理该消息,并且错误应返回给发送方 (,其中包含 soap:MustUnderstand 状态代码) 。 当 mustUnderstand=“0” 或 mustUnderstand 属性不存在时,接收方可以忽略这些标头并继续处理。 mustUnderstand 属性在整个 SOAP 处理模型中起着核心作用。

处理模型

SOAP 定义了一个处理模型,该模型概述了在 SOAP 消息从 SOAP 发送方到 SOAP 接收方时处理 SOAP 消息的规则。 图 1 演示了最简单的 SOAP 消息传送方案,其中一个应用程序 (SOAP 发送方) 将 SOAP 消息发送到另一个应用程序, (SOAP 接收方) 。

但是,处理模型允许使用更有趣的体系结构,如图 3 中的体系结构,其中包含多个 中间 节点。 在本讨论的其余部分,我将使用术语 SOAP 节点 来引用处理 SOAP 消息的任何应用程序,无论是初始发送方、中间方还是最终接收方;否则,我将显式使用精确术语。

图 3. 复杂的 SOAP 消息传送

中介位于初始发送方和最终接收方之间,并截获 SOAP 消息。 中介同时充当 SOAP 发送方和 SOAP 接收方。 通过中间节点,可以设计一些可能受消息内容影响的有趣且灵活的网络体系结构。 SOAP 路由是一个很好的示例,它大量利用 SOAP 中介 (以获取有关 SOAP 路由的详细信息,检查使用 Web 服务增强功能 1.0) 路由 SOAP 消息

在处理消息时,SOAP 节点假定一个或多个影响 SOAP 标头处理方式 的角色 。 角色以 URI) 的形式 (提供唯一名称,以便在处理过程中识别它们。 当 SOAP 节点收到要处理的消息时,它必须首先确定它将承担哪些角色。 它可能会检查 SOAP 消息以帮助做出此决定。

确定要在其中执行操作的角色后,SOAP 节点必须处理所有强制标头, (标记为 mustUnderstand=“1”) 针对其某个角色。 SOAP 节点还可以选择处理任何 (标记为 mustUnderstand=“0”) 针对其某个角色的可选标头。

SOAP 1.1 仅定义名为 https://schemas.xmlsoap.org/soap/actor/next “下一 (”的单个角色 简称为) 。 每个 SOAP 节点都需要承担 下一个 角色。 因此,当 SOAP 消息到达任何给定的 SOAP 节点时,该节点必须处理针对 下一 个角色的所有必需标头,并且可以选择处理也针对 下一个 角色的可选标头。 除了 下一步, SOAP 1.2 还定义了一些其他角色 (请参阅表 3) 和应用程序也允许定义自定义角色。

SOAP 标头通过全局 执行组件 属性以特定角色为目标, (该属性在 SOAP 1.2) 中命名 角色 。 如果执行组件属性不存在,则标头默认以最终接收方为目标。 以下 SOAP 消息演示了如何使用执行组件:

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

由于 wsrp:path 标头面向 下一个 角色,并标记为必需 (mustUnderstand=“1”) ,因此接收此消息的第一个 SOAP 节点需要根据标头块的规范进行处理,在本例中为 WS 路由。 如果 SOAP 节点未设计为了解针对其某个角色的必需标头,则需要生成 SOAP 错误,其中包含 soap:MustUnderstand 状态代码,并停止处理。 SOAP Fault 元素提供 故障函数 子元素,用于指定谁导致消息路径内发生错误。 容错函数属性的值是一个 URI,用于标识导致错误的 SOAP 节点。

如果 SOAP 节点成功处理标头,则需要从消息中删除标头。 允许 SOAP 节点重新插入标头,但这样做会更改协定方 -- 它现在位于当前节点和标头目标下一个节点之间。 如果 SOAP 节点恰好是最终接收方,则它还必须处理 SOAP 正文。

表 3. SOAP 1.2 角色

SOAP 角色名称 说明
http://www.w3.org/2002/06/soap-envelope/role/next 每个 SOAP 中介和最终 SOAP 接收者必须充当此角色,并且 MAY 还承担零个或多个其他 SOAP 角色。
http://www.w3.org/2002/06/soap-envelope/role/none SOAP 节点不得充当此角色。
http://www.w3.org/2002/06/soap-envelope/role/ultimateReceiver 若要将自身建立为最终的 SOAP 接收器,SOAP 节点必须充当此角色。 SOAP 中介不得充当此角色。

协议绑定

图 3 的一个有趣方面是 SOAP 支持通过各种基础协议进行消息交换。 由于 SOAP 消息传送框架独立于基础协议,因此每个中介都可以选择使用不同的通信协议,而不会影响 SOAP 消息。 但是,为了确保在 SOAP 应用程序和基础结构之间实现高级别的互操作性,标准 协议绑定 是必要的。

具体的协议绑定确切地定义了应如何使用给定协议传输 SOAP 消息。 换句话说,它定义了 SOAP 如何适应另一个协议的范围的详细信息,该协议可能具有自己的消息传送框架以及各种标头。 协议绑定实际定义的内容在很大程度上取决于协议的功能和选项。 例如,TCP 的协议绑定与 MSMQ 的协议绑定或 SMTP 的协议绑定大相径庭。

SOAP 1.1 规范仅编纂 HTTP 的协议绑定,因为它用途广泛。 SOAP 已与 HTTP 以外的协议一起使用,但实现未遵循标准化绑定。 只要你准备好在尝试使用相同的协议与其他 SOAP 实现集成后处理互操作性问题,在没有标准协议绑定的情况下继续前进就没有错。

HTTP 绑定

HTTP 协议绑定定义了使用 SOAP over HTTP 的规则。 SOAP 请求/响应自然映射到 HTTP 请求/响应模型。 图 4 演示了许多 SOAP HTTP 绑定详细信息。

图 4。 SOAP HTTP 绑定

在 SOAP 1.2) 中,HTTP 请求和响应消息 的 Content-Type 标头都必须设置为 text/xml (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 正文中封装和表示此信息。 它通过首先定义方法签名如何映射到简单的请求/响应结构(然后可以编码为 XML)来执行此操作。 RPC 绑定指出,方法调用将建模为以 方法命名 的结构 。 结构将包含每个 [in][in/out] 参数的取值器,名称与参数名称相同,按消息签名定义的顺序。 方法响应也将建模为结构。 结构的名称是微不足道的,尽管约定是使用方法名称后跟“Response” (例如,对于 添加 操作,方法响应将相应地命名为 addResponse) 。 响应结构包含返回值的访问器, (名称在 SOAP 1.1 中是微不足道的,但在 SOAP 1.2 中必须是 rpc:result) 后跟每个 [out][in/out] 参数的访问器。

接下来举例说明。 假设 添加操作的 以下 C# 方法签名:

double add(ref double x, double y)

根据刚才所述的 RPC 绑定规则,表示方法调用的请求结构将按以下方式建模:

struct add {
  double x;
  double y;
}

虽然响应结构如下所示:

struct addResponse {
  double result;
  double x;
}

现在的问题是:这些结构应如何映射到 XML? SOAP 规范正是出于此目的定义了一组 编码规则 。 SOAP 编码规则概述了如何将当今最常用的数据结构 ((如结构) 数组)映射到通用 XML 格式。 根据 SOAP 编码规则,上述请求结构将映射到以下 XML 消息, (此消息将放置在 SOAP 正文) :

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

对上述请求的响应消息将映射到以下 XML 消息 (该消息将进入响应消息的正文) :

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

SOAP 编码规则是在 XML 架构工作刚刚开始的时候发明的。 完成 XML 架构后,开发人员只需提供文本 XML 架构定义,即可准确指定在 XML 中设置请求/响应消息的格式。 由于使用 XML 架构定义更容易实现互操作性,因此大多数开发人员已决定完全退出 SOAP 编码规则。 事实上,从 SOAP 1.2 开始,规范不再正式要求支持 SOAP 编码规则。请查看 反对 SOAP 编码的论点 ,全面讨论为什么从此开始最好避免使用 SOAP 编码。

尽管 SOAP RPC 绑定和编码规则为不想弄乱 XML 架构或 WSDL 等内容的应用程序提供了一个很好的 SOAP 集成层,但它们已大为 Web 服务社区所青睐,因为它们更容易出现互操作性问题。

SOAP 样式

重申一下,目前 SOAP 消息传送有两种基本样式:文档和 RPC。 文档样式指示正文仅包含发送方和接收方必须同意其格式的 XML 文档。 另一方面,RPC 样式指示正文包含方法调用的 XML 表示形式,就像我们刚才讨论的一样。

还有两种技术用于决定如何将数据序列化到正文中:使用文本 XML 架构定义和使用 SOAP 编码规则。 使用前一种方法,架构定义从字面上定义了正文的 XML 格式,没有歧义。 但是,使用后一种方法时,SOAP 处理器必须在运行时遍历各种 SOAP 编码规则,以确定正文的正确序列化。 此方法显然更容易出现错误和互操作性问题。

最常见的是将文档样式与文本架构定义 (称为 文档/文本) ,将 RPC 样式与 SOAP 编码规则 (称为 rpc/encoded) 。 文档/编码和 rpc/literal 是可能的,但它们并不常见,没有太大意义。 文档/文本是大多数 Web 服务平台关注的样式,作为未来main用例,它是当今 Microsoft® ASP.NET WebMethod 框架中的默认设置。

总结

SOAP 定义了一个简单且可扩展的 XML 消息传送框架,该框架可用于具有各种不同编程模型的多个协议,尽管该规范编写了如何将 SOAP 与 HTTP 和 RPC 调用配合使用。 SOAP 还定义了一个完整的处理模型,该模型概述了消息在通过路径时如何处理它们。 总的来说,SOAP 提供了一个丰富而灵活的框架,用于定义更高级别的应用程序协议,这些协议在分布式异类环境中提供更高的互操作性。