SOAP 消息有时需要与不同类型的附件一同传输,从法定文书的影印图像到工程图形,这些数据经常是二进制格式。例如,在因特网上传输的图像绝大部分都是 GIF 或 JPEG 格式。本文描述一种标准的方法,将 SOAP 消息与一个或多个附件联系起来,并保留附件的原始格式,作为一个多组分 MIME 结构来传输。这种方法结合了 Multipart/Related MIME 媒体类型的特殊用法和在 RFC2111、RFC2557 中所述的用 URI 引用 MIME 组分的方案,以次来实现 SOAP 消息与相关附件的共同传输。
这里所描述的方法是将多组分 MIME 结构作为传输协议绑定的一个基本组成部分,也就是说,对于 SOAP 消息而言,它等同于传输协议报头。多组分结构,尽管这里给出一个确定的名字�D�D SOAP 消息包,并不是一个可以明确标识的实体,因为并没有标记来明确地标识它是一个实体。在此有意避免增加新的、基于可辨识标记的实体类型。本文的目的在于表述怎样利用现有的 SOAP 工具和标准 MIME 机制来承载和引用附件。换句话来讲,我们用最简单的方法证明了利用现有的技术已经可能实现什么,而不用发明新的东西。对于消息包,更严格的语义需要新的实体类型。这种类型可通过扩展本文所述方法�D�D创建一个新的 SOAP 头条目来实现,比如可以为消息包中的所有内容建立一个清单。
回页首
一个 SOAP 消息包包含一个主体 SOAP1.1 消息,还可包含附加实体�D�D词法上不在 SOAP 消息内,却与之相关。这些实体可以包含非 XML 格式的数据,消息包中的 SOAP1.1 消息可以引用这些附加的实体,这些附加的实体经常被非正式的称为附件。本部分描述怎样构造 SOAP 消息包,及 SOAP 处理器该如何处理他们。
SOAP 消息包是通过 Multipart/Related(在 RFC 2387 中定义)媒体类型来构造的,构造规则如下:
主体 SOAP1.1 消息必需处于 Multipart/Related 结构的根部,因此,Multipart/Related 媒体报头的 type 参数,将总是等于主体 SOAP1.1 消息的 Content-Type 头的值,也就是 text/xml。
被引用的 MIME 组分,必须包含或者是一个 MIME 报头 Content-ID(遵从 RFC 2045),或者是一个 MIME 报头 Content-Location(遵从 RFC 2557)。
强烈建议在根部分包含一个 MIME 头 Content-ID(遵从 RFC 2045),这样就另需要在 Multipart/Related 媒体类型的参数中包含 start 参数。这样能够允许更健壮的错误检测。
符合本规范的 SOAP 处理器,当接收到 SOAP1.1 消息(处于 Multipart/Related MIME 消息的根部)时,必须按照 SOAP1.1 规范中所定义的规定来处理 SOAP 消息。特别地,当 SOAP 处理器接到一条无效的消息时,必须产生如 SOAP1.1 中 4.4.1 节所述的客户错误代码。
使用 MIME Multipart/Related 对 SOAP 消息进行封装,在语义上等同于一个 SOAP 协议绑定,SOAP 消息本身并不知道它是被封装的。也就是说,在主体 SOAP 消息的内部没有任何部分指出该 SOAP 消息是被封装的。
下面的例子是带一个附件的 SOAP1.1 消息,其中附件的内容是一幅关于已签名索赔单的影印图像(claim061400a.tiff)。
MIME-Version: 1.0 Content-Type: Multipart/Related; boundary=MIME_boundary; type=text/xml; start="<[email protected]>" Content-Description: This is the optional message description. --MIME_boundary Content-Type: text/xml; charset=UTF-8 Content-Transfer-Encoding: 8bit Content-ID: <[email protected]> <?xml version='1.0' ?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Body> .. <theSignedForm href="cid:[email protected]"/> .. </SOAP-ENV:Body> </SOAP-ENV:Envelope> --MIME_boundary Content-Type: image/tiff Content-Transfer-Encoding: binary Content-ID: <[email protected]> ...binary TIFF image... --MIME_boundary--
回页首
主体 SOAP 消息中的 Header 条目和 Body 元素及其任一级子元素都可引用消息包中的其他实体。本节阐明一种方法,通过使用 SOAP 和 MIME 中现有的机制就可实现此功能。
根据 SOAP1.1 的数据编码规则,一个 accessor 的值可以通过引用一个 href 属性的值给出,就像一个资源可以通过 URI 来引用一样。我们注意到 SOAP 编码模式允许 href 属性的值可以是任何的 URI,所以该属性不仅可以用来引用 SOAP1.1 消息中的 XML 段,还可以引用其他任何资源。
这里给出 SOAP1.1 中 href 属性的一种使用方式,使属性值是 SOAP 消息包中附件(作为 MIME 的组分)的引用。主体 SOAP1.1 消息中 URI 引用的解析过程,基于 RFC2557 中的部分规定(关于具有 text/html 根文档的多组分 MIME 消息)。我们采用这些关于 HTML 和描述上下文的规则,并将其应用于 SOAP1.1 消息上下文。另外,我们采用 RFC2396 中关于相对 URI 句法及其绝对化规则,而不是已经过时的、RFC2557 中使用的 RFC1808。
解析的过程分两步进行:先将所有的 URI 转化为绝对 URI,然后解析绝对 URI。我们对这两步进行规定。注意,这个处理过程不能应用于 RFC2396 中 4.2 节所定义的同文档引用。SOAP1.1 的语义特征包括通过使用一个带段标识符的 href 属性,来引用同一个 SOAP1.1 消息中的 XML 元素(基于一个保持不变的标签,由 ID 属性定义)。
在 RFC2396 中定义了将相对 URI 引用转化为绝对引用的过程,很具权威性。关于这个过程,我们需要详细说明的是基础 URI 的建立。RFC2396 中规定了建立基础 URI 的过程框架,基于下列次序,按优先级排列。
基础 URI 在文档内:一种机制,明确规定在 SOAP1.1 消息内的基础 URI 将是 XML 基础的机制。
基础 URI 来自封装实体:在封装了主体 SOAP1.1 消息的任何 MIME 实体中,有包含一个绝对 URI 的 Content-Location 报头,那么,最近的 Content-Location 报头中的 URI 就是此实体的基础 URI。
基础 URI 来自回调 URI:为 SOAP 消息包而回调的 URI,不允许被用作基础 URI。
缺省基础 URI:根据 RFC2557,如果不能从上面的方法中得到基础 URI,本消息的基础 URL 将是缺省基础 URI。
构成 SOAP 消息包的 Multipart/Related 结构中,每个 MIME 组分至少有一个绝对 URI 标记,由三种情形:
如果给出的是一个 Content-Location 报头并被赋予一个绝对 URI,那么这个 URI 就是此组分的标记。
如果给出的是一个 Content-Location 报头被赋予一个相对 URI,那么就用上述规则的 2 和 4 来创建基础 URI,并据此将相对 URI 转化为绝对 URI。所得的绝对 URI 便是此组分的标记。
如果给出的是一个 Content-ID 报头,那么此组分的绝对 URI 标记,将按照 RFC2111 中所规定的 CID URI 方案生成。
按如下方式对绝对 URI 进行解析。对于主体 SOAP1.1 消息中的每个 URI,将其按照上述方式转化为绝对 URI 后,与 Multipart/Related 结构中其他组分的绝对 URI 标记(由 Content-ID 和 Content-Location 报头得出)进行比较,比较规则由 RFC2396 给出。如果发现一个匹配,相应 MIME 组分中所包含的实体就是被引用的资源。如果没有匹配,就使用正常的、基于 URI 方案的解析规则。当 Content-ID 和 Content-Location 报头的标记发生冲突时,用 RFC2557 中 8.3 节的规定加以解决,即使用 Content-ID 报头值。
第 2 节的例子说明了在 SOAP 消息的 body 元素中使用 CID 的方法。显然,该例中能够使用远程资源的引用。下面仍是这个例子,但使用了 Content-Location 报头来标识实体,并用绝对 URI 来引用实体:
MIME-Version: 1.0 Content-Type: Multipart/Related; boundary=MIME_boundary; type=text/xml; start="<http://claiming-it.com/claim061400a.xml>" Content-Description: This is the optional message description. --MIME_boundary Content-Type: text/xml; charset=UTF-8 Content-Transfer-Encoding: 8bit Content-ID: <http://claiming-it.com/claim061400a.xml> Content-Location: http://claiming-it.com/claim061400a.xml <?xml version='1.0' ?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Body> .. <theSignedForm href="http://claiming-it.com/claim061400a.tiff"/> .. </SOAP-ENV:Body> </SOAP-ENV:Envelope> --MIME_boundary Content-Type: image/tiff Content-Transfer-Encoding: binary Content-ID: <http://claiming-it.com/claim061400a.tiff> Content-Location: http://claiming-it.com/claim061400a.tiff ...binary TIFF image... --MIME_boundary �D
下面是同一个例子,这次使用了相对 URI,并使用 MIME Multipart/Related 结构基础部分的 Content-Location 报头的值最为它们的基础 URI:
MIME-Version: 1.0 Content-Type: Multipart/Related; boundary=MIME_boundary; type=text/xml; start="<http://claiming-it.com/claim061400a.xml>" Content-Description: This is the optional message description. Content-Location: http://claiming-it.com/ --MIME_boundary Content-Type: text/xml; charset=UTF-8 Content-Transfer-Encoding: 8bit Content-ID: <http://claiming-it.com/claim061400a.xml> Content-Location: claim061400a.xml <?xml version='1.0' ?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Body> .. <theSignedForm href="claim061400a.tiff"/> .. </SOAP-ENV:Body> </SOAP-ENV:Envelope> --MIME_boundary Content-Type: image/tiff Content-Transfer-Encoding: binary Content-Location: claim061400a.tiff ...binary TIFF image... --MIME_boundary--
最后这个例子使用了相对 URI,但并没有明确声明基础 URI,这样就会用到规则 4 来建立一个基础 URI。此时,SOAP 消息中的相对 URI 和 Content-Location 标记都要用到此基础 URI:
MIME-Version: 1.0 Content-Type: Multipart/Related; boundary=MIME_boundary; type=text/xml; start="<[email protected]/s445>" Content-Description: This is the optional message description. --MIME_boundary Content-Type: text/xml; charset=UTF-8 Content-Transfer-Encoding: 8bit Content-ID: <[email protected]/s445> Content-Location: claim061400a.xml <?xml version='1.0' ?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Body> .. <theSignedForm href="the_signed_form.tiff"/> .. </SOAP-ENV:Body> </SOAP-ENV:Envelope> --MIME_boundary Content-Type: image/tiff Content-Transfer-Encoding: binary Content-ID: <[email protected]/s445> Content-Location: the_signed_form.tiff ...binary TIFF image... --MIME_boundary-
值得注意的是,在 SOAP 消息中,作为 href 属性的值而出现的 URI 引用,并未暗示接收 SOAP 处理器必需解析该 URI。SOAP 处理器可以根据消息的处理语义来决定是否需要解析 URI。接收 SOAP 处理器可以选择忽略该 URI,尽管它是用来引用一个 MIME 附件。相反地,消息包所有的附件也可能均不被 SOAP 消息所引用。
回页首
带附件的 SOAP 消息包是对 SOAP1.1 所定义的传输绑定机制的一个扩展。将 SOAP1.1 消息作为 Multipart/Related MIME 结构的根组分,与其他内容一同打包,可以看作是通过任何能够传输 MIME 内容的通信协议,承载 SOAP1.1 消息的一种特殊方法。SOAP 处理器要同时支持 MIME 编码格式和底层通信协议,并且在处理 SOAP1.1 消息时,必须遵循 SOAP1.1 中所有关于消息及底层传输绑定的规定。
回页首
因为是基于 SOAP1.1 的,所以这里并不描述异步消息、或同步请求 / 响应交互模式。这里所讲的 HTTP 绑定只涉及到在构建 SOAP 消息包时,HTTP 报头与 MIME 报头之间的关系,而对于交互模式丝毫没有涉及。
在 HTTP 消息中承载多组分 MIME 结构的基本方法是:限定用 MIME 编码的内容于 MIME 组分内,并在 HTTP 本身报头中使用 Multipart/Related 媒体类型。在 HTTP 消息中包含 SOAP 消息包的规则如下:
MIME 报头 Content-Type:Multipart/Related 必须作为一个 HTTP 报头出现,在第 2 节中所规定的关于此报头中参数的规定在这里也适用。
在 MIME 规范中定义的其他含义报头(如 Content-Transfer-Encoding)不能作为 HTTP 报头出现。特别是“MIME-Version: 1.0”报头,绝对不能作为 HTTP 报头出现。注意,在 HTTP1.1 中定义了许多与 MIME 类似的报头,有具体的含义。这些报头当然可以自由出现。
包含了 SOAP 消息和附件的各 MIME 组分,构成了 HTTP 消息体,并且按第 2 节中所述的方式表示,包括恰当的 MIME 报头。
下面的例子是一则 HTTP 消息,包含一个 SOAP 消息包,包内含有两个附件构成一则汽车保险索赔。SOAP1.1 消息内包含索赔数据,与一个已签署索赔单的影印图像(claim.tiff)和一幅被毁坏汽车的数字照片(car.jpeg)一同发送。
POST /insuranceClaims HTTP/1.1 Host: www.risky-stuff.com Content-Type: Multipart/Related; boundary=MIME_boundary; type=text/xml; start="<[email protected]>" Content-Length: XXXX SOAPAction: http://schemas.risky-stuff.com/Auto-Claim Content-Description: This is the optional message description. --MIME_boundary Content-Type: text/xml; charset=UTF-8 Content-Transfer-Encoding: 8bit Content-ID: <[email protected]> <?xml version='1.0' ?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Body> <claim:insurance_claim_auto id="insurance_claim_document_id" xmlns:claim="http://schemas.risky-stuff.com/Auto-Claim"> <theSignedForm href="cid:[email protected]"/> <theCrashPhoto href="cid:[email protected]"/> <!-- ... more claim details go here... --> </claim:insurance_claim_auto> </SOAP-ENV:Body> </SOAP-ENV:Envelope> --MIME_boundary Content-Type: image/tiff Content-Transfer-Encoding: base64 Content-ID: <[email protected]> ...Base64 encoded TIFF image... --MIME_boundary Content-Type: image/jpeg Content-Transfer-Encoding: binary Content-ID: <[email protected]> ...Raw JPEG image.. --MIME_boundary--
回页首
通过利用 SOAP 和 MIME 自身的功能,就能实现 SOAP 消息与相关实体的绑定,达到一同传输的目的。本文详细介绍了消息格式、对附件的引用方法以及相应 SOAP 处理器的处理规则,最后给出了 SOAP 消息包通过 HTTP 协议传时的具体消息格式。本文所介绍的 SOAP 消息与附件的绑定方法,适用于各种格式的附件,必将对 B2B 消息互换产生巨大的促进作用。
参考文献
[SOAPATTACH] SOAP Messages with Attachments( http://www.w3.org/TR/2000/NOTE-SOAP-attachments-20001211)
[SOAP] Simple Object Access Protocol (SOAP) Version 1.1 (http://www.w3.org/TR/SOAP/)
[XML] Extensible Markup Language (XML) 1.0 (http://www.w3.org/TR/1998/REC-xml-19980210)
[MultipartRelated] The MIME Multipart/Related Content-type (http://www.ietf.org/rfc/rfc2387.txt)
[MIME1] Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies (http://www.ietf.org/rfc/rfc2045.txt)
[CID] Content-ID and Message-ID Uniform Resource Locators http://www.ietf.org/rfc/rfc2111.txt
[URI] Uniform Resource Identifiers (URI): Generic Syntax (http://www.ietf.org/rfc/rfc2396.txt)
[RFC2557] MIME Encapsulation of Aggregate Documents, such as HTML (MHTML) (http://www.ietf.org/rfc/rfc2557.txt)
[XMLBASE] XML Base http://www.w3.org/TR/xmlbase
[HTTP] Hypertext Transfer Protocol -- HTTP/1.1 (http://www.ietf.org/rfc/rfc2616.txt)
[SMTP] Simple Mail Transfer Protocol (http://www.ietf.org/rfc/rfc0821.txt)