`
li-yuan
  • 浏览: 66536 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论
阅读更多

1.7     简单对象访问协议 (SOAP)

OAP Web 服务消息传输的技术标准。SOAP 是基于XML 的协议,由三个部分组成:

1、 信封,描述消息是什么以及如何处理;

2、 一组编码规则,用于表示由应用程序定义的数据类型的实例;

3、 有关远程过程调用及响应表示的约定;

SOAP 可以与其他多种协议结合使用,但这里只描述了如何结合使用SOAPHTTP 。图1.4.1 展示了SOAP 请求的整体结构。



 1.7.1 SOAP HTTP 请求整体结构

1.7.1  HTTP 协议层

按照 SOAP 规范定义 SOAP 请求需要增加一个 HTTP 协议头 SOAPAction ,用于表示该请求为 SOAP 协议请求,它的值是目标网络端点的 URI ,但是没有对该 URI 的格式、 URI 特性和可解析性做任何限制,当客户通过 HTTP 发送 SOAP 请求时必须使用在 HTTP 头中使用这个头,如果这个头的值为空字符串,那么表明该请求的 URI HTTP 的一致。

1.7.2  SOAP 信封

XML 标签EnvelopeSOAP 协议不可缺少的部分,也是SOAP 协议的根标签, 还需要根据名字空间指定SOAP 的版本。如图1.4.1 示例那样指定SOAP 的版为1.2 ,所有用的名字空间为:

http://www.w3.org/2003/05/SOAP-envelope

  1.7.3  SOAP 协议头

SOAP 协议头在 SOAP 消息中是可选的。如果需要头,必须是 SOAP 封装元素的第一个直接子元素。 SOAP 头可以包含多个条目,每个都是 SOAP 头元素的直接子元素。所有 SOAP 头的直接子元素都必须指定名字空间。 SOAP 协议头可以用来设置扩展信息,例如 SOAP 协议头可以用于安全认证,可以将用户的角色信息加入 SOAP 头中, Web 服务可以根据 SOAP 协议头中用户的角色信息来判断是否处理该请求,如清单 1.4.3 Head 中指定了用户的角色。

清单 1.4.1 Head 示例

<SOAP-ENV:Envelope
      xmlns: SOAP-ENV="http://www.w3.org/2003/05/soap-envelope">
< SOAP-ENV:Header>
       <lovo:auth xmlns:lovo="http://www.lovoinfo.com">
           <lovo:user-role>root</lovo:user-role>
       </lovo:auth>


</ SOAP-ENV:Header>
<SOAP-ENV:Body>
……
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
 

1.7.4  SOAP 协议体

      发送 SOAP 消息时,都是有目的性的。需要告诉接收者执行某种操作,或尝试向服务器传递相关信息。此信息称为“有效负载”。有效负载位于 SOAP Body 标记中。它还具有自己的命名空间,在本例中其命名空间与一个网上书店系统对应,在此情况下,可以完全随意地选择命名空间。只需要与 SOAP 命名空间相异即可(请参见清单 1.4.2 )。

清单 1.4.2 SOAP 有效负载

<?xml version='1.0' ?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/SOAP-envelope"> 
 <env:Header>
     ...
 </env:Header>
 <env:Body>
   <store:addBook xmlns:store="http://www.lovoinfo.com/book">
    <store:category>计算机</store:category>
    <store:subcategory>Java</store:subcategory>
    <store:author></store:author>
    <store:bookName>Thinking in Java</store:bookName>
    </store:addBook>

 </env:Body>
</env:Envelope>

在此例中,有效负载很简单,其中包含将书添加到网上书店系统的操作请求。如何设计有效负载的选择过程将涉及到 SOAP 的样式和编码。

1.7.5  SOAP 错误响应

上一节中只说明了正常情况下的 SOAP 消息 BODY 中的有效负载,但是系统出现错误和异常是难免的, SOAP 需要制定专门的机制来处理这些异常,例如客户端发送了一个的 SOAP 请求缺少了必须的内容,服务器端需要通过一个特殊的 SOAP 应答来告诉客户端发生了错误, SOAP 协议使用一个 Fault 元素作为 Boby 的第一个子元素, Fault 则相应的包含了出错的具体信息,下面清单 1.4.3 就是一个 SOAP 协议的错误响应。

清单 1.4.3 SOAP 协议的错误响应

<?xml version='1.0' ?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/SOAP-envelope"> 
 <env:Header>
  ...
 </env:Header>
 <env:Body>
   <env:Fault>
      <faultcode>Client</faultcode>
      <faultstring>incorrect information</faultstring>
      <faultactor>Undefine</faultactor>
      <detail>
        The request message was contained incorrect information
      </detail>
   </env:Fault>
 </env:Body>
</env:Envelope>


 

Fault 元素可以有如下的子元素:

子元素

描述

<faultcode>

供识别故障的代码

<faultstring>

可供人阅读的有关故障的说明

<faultactor>

有关是谁引发故障的信息

<detail>

保存涉及 Body 元素的应用程序专用错误信息

1.7.6  SOAP 样式

    在介绍 WSDL 部分将更深入地了解此主题的内容,但在创建应用程序时,您将需要确定要发送和接收的实际有效负载的结构。为此,需要了解一下编程样式和编码。

简单来说,有两种不同的主流 Web 服务编程样式。第一种是 RPC 样式,在此样式中,基本思路是在向服务器发送命令调用方法(如“添加新书”),往往用 Body 元素的第一子元素表示方法名,并将该命令的参数(如要添加的新书和应该添加到的类别)作为整个方法的子元素包含在其中,如清单 1.4.2 中所示。 RPC 样式的替代方法将数据直接作为 SOAP 体的内容处理(请参见清单 1.4.4 )。

清单 1.4.4 文档样式的 SOAP

<?xml version='1.0' ?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/SOAP-envelope">
    <env:Header>
      ...

    </env:Header>
    <env:Body>
      <newBook>
        <category>计算机</ category>
        <subcategory>Java</subcategory>
        <author></author>
        <bookName>Thinking in Java</bookName>
      </newBook>
    </env:Body>
</env:Envelope>


 

在这种情况下,消息本身并不包含有关数据所提交到的进程的信息,此工作由路由软件进行。例如,所有对特定 URL 或端点的调用都可能指向特定的操作。

1.7.7  SOAP 消息交互模式

发送消息的方式有很多选择,可以发送请求并等待响应,发送请求但不等待响应,发送请求并在到达最终的目的地前通过多个中间层。但就实质而言,只有两个选择:

  • 请求/ 响应 :在请求/ 响应模式种,以 SOAP 消息的形式发送请求,然后直接等待发送回响应。请求可以为同步的,也可以是异步的。
  • 单向消息传递 :发送请求但并不等待响应。可以在仅传递信息时或并不关心接收者对此如何响应时使用此方法。

现 在,请注意并没有使用术语 客户机 服务器 。之所以这样,是因为这些消息交换模式几乎可以用于创建与上面提到的方法类似的任意数量的不同备选方法。例如,可以发送一条请求,然后依靠接收者对其进行处理,并在将来完成应完成的工作时发送一条消息。为此,将使用多个单向消息的组合,因此谈 客户机 服务器 并不合理,因为每个消息都有其接收方和发送方,所谓的客户机和服务器的位置会发生对换。

1.8     WEB 服务描述语言 (WSDL)

Web 服务的目的是为了解决企业级应用中的异构系统之间的通信问题,要实现通信首先明确通信的消息是什么,格式如何,然后需要对消息中所包含数据的类型进行说明,接着需要说明具体提供服务的接,最后是说明接口所包含操作与消息的关系。作为异构系统的说明书, WSDL 需要能够被多种技术平台和语言读懂,所以采用了 XML 作为 WSDL 格式。图 1.8.1 展示了 WSDL 的基本结构。


1.8.1WSDL 基本结构

清单 1.5.1 展示了 WSDL 文档的基本结构


1.8.1  数据类型

这里要定义 XML 的数据类型,毫无疑问将使用 XSD 来进行定义。例如网上书店有一个 Web 服务提供给出版社用于添加新书,这个 Web 服务采用 RPC 样式,那么这个服务的 WSDL 数据类型定义部分如清单 1.5.2

清单 1.5.2 网上书店添加新书的数据定义

<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
          xmlns:ns1="http://www.lovolinfo.com/bookstore" 
       targetNamespace="http://www.lovolinfo.com/bookstore/xsd">
  <wsdl:types>
    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
         targetNamespace="http://www.lovolinfo.com/bookstore/xsd" 
         elementFormDefault="unqualified"
         attributeFormDefault="unqualified">
        <xs:element type="ns1:AddNewBook" name="AddNewBook" />
        <xs:complexType name="AddNewBook">
           <xs:sequence>
              <xs:element type="xs:string" name="category" />
              <xs:element type="xs:string" name="subcategory" />
              <xs:element type="xs:string" name="author" />
              <xs:element type="xs:string" name="bookName" />
           </xs:sequence>
        </xs:complexType>
    </xs:schema>
  </wsdl:types>
</wsdl:definitions>
 

从代码的起始处开始,请注意有两个命名空间部分。第一个位于 schema 元素本身中。此处定义了两个命名空间。第一个命名空间是 XML Schema 命名空间,使用前缀 xs: 。第二个命名空间是 targetNamespace ,定义模式创建的定义所属的命名空间。也就是说,当第二个版本创建名为 AddNewBook complexType 时,该定义属于命名空间 http://www.lovolinfo.com/bookstore/xsd 。不过,为了引用该命名空间,需要创建另一个别名。可以在以 ns1: 为前缀的 definitions 元素上看到此别名。后面两个属性 elementFormDefault attributeFormDefault 表示元素和属性是否应该有命名空间前缀。

XML 中,经常有必要为各种元素和属性指定“命名空间”。这样就能方便地对具有相同名称但用途不同(来源也可能不同)的元素进行区分。 XML 通过 URI 引用命名空间。例如, XML 模式命名空间为 http://www.w3.org/2001/XMLSchema 。不过,为了方便起见,还为其分配了一个别名(或前缀)。例如,此处的模式命名空间的前缀为 xs: 。请记住,别名只是一个别名而已,重要的是 URI 。因此,属于 ns1:  命名空间的元素或属性也是模式的 targetNamespace 的一部分。

1.8.2  定义消息

定义好了数据类型,接下来就是定义消息,我们将在清单 1.5.2 的基础上增加上有关消息的定义,如清单 1.5.3 为增加新书服务定义了请求消息和应答消息,相应的也增加了应答消息的数据类型定义。

清单 1.5.3 定义消息

<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
       xmlns:ns1=" http://www.lovolinfo.com/bookstore" 
       targetNamespace="http://www.lovolinfo.com/bookstore/xsd">

  <wsdl:types>

    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 

         targetNamespace="http://www.lovolinfo.com/bookstore/xsd" 

         elementFormDefault="unqualified" 

         attributeFormDefault="unqualified">
        <xs:element type="ns1:AddNewBook" name="AddNewBook" />
        <xs:complexType name="AddNewBook">
           <xs:sequence>
              <xs:element type="xs:string" name="category" />
              <xs:element type="xs:string" name="subcategory" />
              <xs:element type="xs:string" name="author" />
              <xs:element type="xs:string" name="bookName" />
           </xs:sequence>
        </xs:complexType>
        <xs:element type="ns1:AddNewBookResult" name="AddNewBookResult" />
        <xs:complexType name="AddNewBookResult">
           <xs:sequence>
              <xs:element type="xs:string" name="result" />
          </xs:sequence>
        </xs:complexType>
    </xs:schema>
  </wsdl:types>
  <wsdl:message name="AddNewBookRequestMessage">
     <wsdl:part name="part1" element="ns1:AddNewBook"/>
  </wsdl:message>
  <wsdl:message name="AddNewBookResponseMessage">
     <wsdl:part name="part1" element="ns1:AddNewBookResult"/>
  </wsdl:message>
</wsdl:definitions>
 

每个消息都具有一个 name ,以便稍后对其进行引用。在每个消息内,您可以定义一个或多个 part 。请注意, WSDL 2.0 仅允许每个消 息包含一个 part ,因此此处严格遵循了此约定。

每个 part 都有一个 name ,而由 element 的名称组成该 part 。元素名称将反过来引用在数据类型中定义的类型。请注意,元素的名称以 ns1: 为前缀,即与模式的 targetNamespace 匹配的命名空间前缀。也就是说,当创建 AddNewBookResponse 的定义时,该定义进入了 http://www.lovolinfo.com/bookstore/xsd 名字空间中,由于前缀 ns1: 也引用此命名空间,因此现在可以使用该前缀引用此命名空间。

 

1.8.3  接口定义

仅仅有数据类型和消息还能完成对服务的定义,需要描述 Web 服务提供那些操作(方法)来处理这些消息。而 WSDL 的接口定义部分就是用于描述 Web 服务所提供操作(方法)。如清单 1.5.4 增加了对网上书店增加新书服务提供的操作进行了定义。

 

清单 1.5.4 接口定义

<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 

       xmlns:ns1=" http://www.lovolinfo.com/bookstore" 

       targetNamespace="http://www.lovolinfo.com/bookstore/xsd">

  <wsdl:types>

     ... ...

  </wsdl:types>

  <wsdl:message name="AddNewBookRequestMessage">
     <wsdl:part name="part1" element="ns1:AddNewBook"/>
  </wsdl:message>
  <wsdl:message name="AddNewBookResponseMessage">
     <wsdl:part name="part1" element="ns1:AddNewBookResult"/>
  </wsdl:message>
<wsdl:portType name="NewBookPortType">
    <wsdl:operation name="AddNewBook">
       <wsdl:input message="tns:AddNewBookRequestMessage" />
       <wsdl:output message="tns:AddNewBookResponseMessage" />
    </wsdl:operation>
  </wsdl:portType>
</wsdl:definitions>
 

这里描述了新书服务的一个增加新书的操作,这是一个请求 / 应答型的操作,如果去掉 <wsdl:output message="tns:AddNewBookResponseMessage" /> 则成了一个单路的操作。

1.8.4  定义绑定

我们已经定义了消息以及服务提供的操作,但是还是没有明确每个操作的具体实现,采用什么样的服务样式以及使用何种协议。清单 1.5.5 展示将新书服务中的增加新书操作与 SOAP 协议绑定在一起并定义了将采用文档的服务方式而不是 RPC 的方式。

清单 1.5.5 定义绑定

<wsdl:binding name="NewBookServiceBinding" 
                       type="tns:NewBookPortType">
   <soap:binding transport="http://schemas.xmlsoap.org/soap/http" 
                                              style="document" />
      <wsdl:operation name="AddNewBook">
         <soap:operation soapAction="AddNewBook" style="document"/>
         <wsdl:input>
                 <soap:body use="literal" 
                    namespace="http://www.lovoinfo.com/book"/>
         </wsdl:input>
         <wsdl:output>
                 <soap:body use="literal" 
                    namespace="http://www.lovoinfo.com/book" />
         </wsdl:output>
      </wsdl:operation>
   </soap:binding>               
</wsdl:binding>
 

首先,请注意绑定 type 引用   已经创建的 NewBookPortType 。其次,请注意添加了 soap:  命名空间。此 binding 用于 HTTP 上的 SOAP 消息,如 soap:binding 元素中所示。请注意 transport 属性。对于共享其在 portType 中的名称的每个操作, 现在都要添加一个 soap:operation 元素。此元素执行两个操作。根据此元素的说明,第一个操作实际上是 SOAP 操作。第二个操作用于指定 soapAction ,此为在实际 SOAP 消息前发送的 HTTP 协议头。服务器可以使用此协议头来将请求路由到相应的操作。请注意, soapAction 始终有一定的问题,虽然在 WSDL 1.1 是可选的,但已经完全从 WSDL 2.0 中删除了。每个操作还定义 input output 消息(或者,使用仅输入的消息时,只定义 input 消息),但现在   还将添加特定的 SOAP 信息。此元素还为有效负载的内容定义命名空间。

特别需要说明的是 operation 标记的 style 属性和 body 标记的 use, 这两个属性决定着这个 WEB 服务所使用的样式和编码规则。正如上文所提到的那样, SOAP 有两种使用样式一种为 RPC 另一种为文档模式,这里的 style 就是来指出该操作上所有绑定的 SOAP 是属于那种使用样式。而 use 属性是用来指定 SOAP body 中有效载荷的编码,其取值有两个:

  • Literal, 表明 SOAP body 中的内容是没有编码的,仅仅是一个 xml 文档而已;  
  • Encode :表明 SOAP bocy 中的内容是有一定编码规则的。

style use 取不同的值,就可以组合出多种的 SOAP 协议样式和编码,因为具体的样式和编码都是开发框架和 Web 服务运行环境来决定的,对于 Web 服务的开发和使用者来说做到有所了解就可以了

1.8.5  服务定义

最后,还需要对整个 Web 服务进行说明。服务定义包含了定义服务的 URL 采用具体的那个绑定,清单 1.5.6 是增加新书服务的定义。

清单 1.5.6 增加新书服务定义

<wsdl:service name="NewBookService">
  <wsdl:port name="NewBookPort" binding="tns:NewBookServiceBinding">
    <soap:address location="http://www.lovoinfo.com/bookstore" />
  </wsdl:port>
</wsdl:service>
 

服务可以具有多个端点,每个端点都由其自己对应的 port 元素进行定义。 port 元素与特定绑定对应,包括有关如何访问此绑定的信息。在本例中,指定可以通过 SOAP 在以下位置访问该端口: http://www.lovoinfo.com/bookstore

至此,我们已经对 Web 服务的两个重要规范有了一定的了解,接下来,我们将采用契约优先的方式通过 spring-ws 框架构建一个 WEB 服务以加深对 WEB 服务的理解。

 

  • 大小: 52.1 KB
  • 大小: 14.5 KB
  • 大小: 35 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics