欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

把 SOAP 服务转化为 REST 服务(REST Service 的最佳实践,第 3 部分)

发布时间:2025/7/25 编程问答 42 豆豆
生活随笔 收集整理的这篇文章主要介绍了 把 SOAP 服务转化为 REST 服务(REST Service 的最佳实践,第 3 部分) 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

from: https://www.ibm.com/developerworks/cn/webservices/1102_mace_restservicePart3/1102_mace_restservicePart3.html?ca=drs-

基于 SOAP 的 Web 服务和 REST 服务的描述

在本系列的前两篇文章中,作者系统的介绍了 REST 服务的核心概念以及 REST 和 SOAP 服务的实现机理。接下来,我们以获取股价的 Web 服务为例,来看看基于 SOAP 的 Web 服务和 REST 服务的描述、发送请求的方式和响应的格式的不同。

清单 1 所示是一个获取股价的基于 SOAP 协议的 Web 服务。如果不熟悉 WSDL 规范的朋友请参考文献,我们这里不再详述。描述文件看起来很复杂,其实就是两个服务端点,在 service 元素里面描述的两个:StockQuoteSoap、StockQuoteHttpGet。StockQuoteSoap 说明这个服务端点接受 SOAP 协议的的请求并在 SOAP body 里面返回服务的结果。StockQuoteHttpGet 是以 SOAP over HTTP 的方式提供服务。另外还有对端口类型、绑定、消息、输入参数、输出参数的描述,有点像对一个函数签名的详细描述。

清单 1.WSDL 描述的获取股价的 Web 服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 <?xml version="1.0" encoding="utf-8"?> <wsdl:definitions xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:s="http://www.w3.org/2001/XMLSchema"  xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"  xmlns:tns="http://www.webserviceX.NET/"  xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"  xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"  targetNamespace="http://www.webserviceX.NET/"  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">  <wsdl:types>    <s:schema elementFormDefault="qualified"    targetNamespace="http://www.webserviceX.NET/">      <s:element name="GetQuote">        <s:complexType>          <s:sequence>            <s:element minOccurs="0" maxOccurs="1"            name="symbol" type="s:string" />          </s:sequence>        </s:complexType>      </s:element>      <s:element name="GetQuoteResponse">        <s:complexType>          <s:sequence>            <s:element minOccurs="0" maxOccurs="1"             name="GetQuoteResult" type="s:string" />          </s:sequence>        </s:complexType>      </s:element>      <s:element name="string" nillable="true" type="s:string" />    </s:schema>  </wsdl:types>  <wsdl:message name="GetQuoteSoapIn">    <wsdl:part name="parameters" element="tns:GetQuote" />  </wsdl:message>  <wsdl:message name="GetQuoteSoapOut">    <wsdl:part name="parameters" element="tns:GetQuoteResponse" />  </wsdl:message>  <wsdl:message name="GetQuoteHttpGetIn">    <wsdl:part name="symbol" type="s:string" />  </wsdl:message>  <wsdl:message name="GetQuoteHttpGetOut">    <wsdl:part name="Body" element="tns:string" />  </wsdl:message>  <wsdl:portType name="StockQuoteSoap">    <wsdl:operation name="GetQuote">      <documentation xmlns      ="http://schemas.xmlsoap.org/wsdl/">Get Stock quote for a company Symbol      </documentation>      <wsdl:input message="tns:GetQuoteSoapIn" />      <wsdl:output message="tns:GetQuoteSoapOut" />    </wsdl:operation>  </wsdl:portType>  <wsdl:portType name="StockQuoteHttpGet">    <wsdl:operation name="GetQuote">      <documentation xmlns      ="http://schemas.xmlsoap.org/wsdl/">Get Stock quote for a company Symbol      </documentation>      <wsdl:input message="tns:GetQuoteHttpGetIn" />      <wsdl:output message="tns:GetQuoteHttpGetOut" />    </wsdl:operation>  </wsdl:portType>  <wsdl:binding name="StockQuoteSoap" type="tns:StockQuoteSoap">    <soap:binding transport="http://schemas.xmlsoap.org/soap/http"     style="document" />    <wsdl:operation name="GetQuote">      <soap:operation soapAction="http://www.webserviceX.NET/GetQuote"      style="document" />      <wsdl:input>        <soap:body use="literal" />      </wsdl:input>      <wsdl:output>        <soap:body use="literal" />      </wsdl:output>    </wsdl:operation>  </wsdl:binding>  <wsdl:binding name="StockQuoteHttpGet" type="tns:StockQuoteHttpGet">    <http:binding verb="GET" />    <wsdl:operation name="GetQuote">      <http:operation location="/GetQuote" />      <wsdl:input>        <http:urlEncoded />      </wsdl:input>      <wsdl:output>        <mime:mimeXml part="Body" />      </wsdl:output>    </wsdl:operation>  </wsdl:binding>  <wsdl:service name="StockQuote">    <wsdl:port name="StockQuoteSoap" binding="tns:StockQuoteSoap">      <soap:address location="http://www.webservicex.net/stockquote.asmx" />    </wsdl:port>    <wsdl:port name="StockQuoteHttpGet" binding="tns:StockQuoteHttpGet">      <http:address location="http://www.webservicex.net/stockquote.asmx" />    </wsdl:port>    </wsdl:service> </wsdl:definitions>

根据这个服务的描述,我们来看一下怎么调用这个服务。清单 2 和清单 3 给出了调用示例和响应示例。根据描述我们知道,SOAPAction 是 GetQuote,HTTP method 是 GET,这个服务的输入参数是一个 String 类型的股票代码,如 IBM,参数名称是 symbol,服务的端点是 www.webservicex.net/stockquote.asmx。首先如清单 2 所示构建 StockQuoteHttpGet 服务的请求。

清单 2. A SOAP Request 示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 GET /stockquote.asmx HTTP/1.1 Host: www.webservicex.net Content-Type: text/xml; charset="utf-8" Content-Length: nnn SOAPAction= "http://www.webserviceX.NET/GetQuote" <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"  xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">  <soap:Body>    <GetQuote xmlns="http://www.webserviceX.NET/">      <symbol>IBM</symbol>    </GetQuote>  </soap:Body> </soap:Envelope>

清单 3 返回的是按照 SOAP 协议封装的调用响应,在 SOAP body 里面,GetQuoteResult 里面放置的是调用结果,返回的是 XML 表示的 IBM 在调用时刻的股价信息,

清单 3. A SOAP response 示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 HTTP/1.1 200 OK Content-Type: text/xml; charset='utf-8' Content-Length: nnn <?xml version="1.0" encoding="UTF-8"?> <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">  <soap:Body>    <GetQuoteResponse xmlns="http://www.webserviceX.NET/">      <GetQuoteResult> <StockQuotes> <Stock> <Symbol> IBM </Symbol> <Last> 144.36 </Last> <Date> 11/18/2010 </Date> <Time> 4:00pm </Time> <Change> 0.00 </Change> <Open> N/A </Open> <High> N/A </High> <Low> N/A </Low> <Volume> 0 </Volume> <MktCap> 179.3B </MktCap> <PreviousClose> 144.36 </PreviousClose> <PercentageChange> 0.00% </PercentageChange> <AnnRange> 116.00 - 147.53 </AnnRange> <Earns> 11.001 </Earns> <P-E> 13.12 </P-E> <Name> International Bus </Name> </Stock> </StockQuotes> </GetQuoteResult>    </GetQuoteResponse>  </soap:Body> </soap:Envelope>

从清单 2 和清单 3 可以看出,基于 SOAP 的 Web 服务把 SOAP 请求和 SOAP 响应封装在 soap Envelope 中,服务的调用端需要自己构建这个 SOAP 信封,并且需要一定的 code 去做解析工作。一般来说,XML 的解析是一项复杂度比较高的任务,比较耗时,这将会影响整个程序的性能。

下面我们来看一下以 REST 服务的方式怎么提供和清单 1 对应的股票查询的能力。首先我们还是来看一下服务的描述,如清单 4 所示。

清单 4. 获取股价的 REST 服务的描述
1 2 3 4 5 6 7 8 <?xml version="1.0" encoding="UTF-8"?> <service xmlns="http://www.ibm.com/rest/description/1.0/">  <title> Stock quote for a company Symbol </title>  <template httpMethod="GET" url=" http://www.webservicex.net/stockquote.asmx/GetQuote?symbol={symbol}"/>  <parameter name="symbol" required="true"  defaultValue="IBM" style="template"/> </service>

和清单 1 比较,清单 4 显得特别简洁明了,语义也特别清楚。这给程序员的处理程序很大的简化的可能性。清单 5 和清单 6 显示了获取股价的 REST 服务的调用。从清单 5 可以看出,请求的发送非常的简单,仅仅是一个 HTTP url,而清单 6 显示的查询结果要清单 3 的查询结果看起来语义要清楚很多。

清单 5. A REST Request over HTTP 示例
1 2 GET /stockquote.asmx/GetQuote?symbol=IBM   HTTP/1.1 Host: www.webservicex.net
清单 6. A REST Response over HTTP 示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 HTTP/1.1 200 OK Date: Fri, 12 Sept 2010 17:15:33 GMT Last-Modified: Fri, 12 Sept 2010 17:15:33 GMT ETag: "2b3f6-a4-5b572640" Accept-Ranges: updated Content-Type: text/xml; charset="utf-8" <StockQuotes> <Stock> <Symbol> IBM </Symbol> <Last> 144.36 </Last> <Date> 11/18/2010 </Date> <Time> 4:00pm </Time> <Change> 0.00 </Change> <Open> N/A </Open> <High> N/A </High> <Low> N/A </Low> <Volume> 0 </Volume> <MktCap> 179.3B </MktCap> <PreviousClose> 144.36 </PreviousClose> <PercentageChange> 0.00% </PercentageChange> <AnnRange> 116.00 - 147.53 </AnnRange> <Earns> 11.001 </Earns> <P-E> 13.12 </P-E> <Name> International Bus </Name> </Stock> </StockQuotes>

分析 SOAP 的 Web 服务和 REST 服务的关系

现在你被认为已经清楚了基于 SOAP 的 Web 服务和 REST 服务的描述,以及已经会调用他们。接下来,我们来看一下这两种服务的逻辑关系。

  • 面向方法和面向资源。从清单 1 可以看出,SOAP 服务是按照面向方法的方法论来设计的,需要服务提供者清楚的给出每个方法的名称、输入参数、输出详细描述、绑定等等,这些又再次封装在消息 message 中。而从清单 4 中我们可以看出,REST 服务是面向资源的,服务提供者只需要告诉用于定位到服务的 URL template 以及要实例化这个 template 所有的参数描述。为了使这个服务可以工作,所以这里我们用了 http://www.webservicex.net/stockquote.asmx/GetQuote?symbol={symbol},但是更好的 URL 格式应该是 http://www.webservicex.net/stockquote.asmx/Quote?symbol={symbol},也许你已经发现了,两个 URL 只是 GetQuote 和 Quote 的差别。奥妙就在这。GetQuote 看起来像一个方法名称,而 Quote 是一个名词,是一个资源。知道了这个差别,可以把 SOAP 服务的输出作为一种资源,对应提供 REST 服务。
  • 参数对应。在 SOAP 描述文件中我们看到调用一个 SOAP Action 所需要的输入的详细描述。这些参数是系统提供服务所要求的必须的输入。而在 REST 服务中,用户看到的就是一个 URL,所以,我们可以把 SOAP Action 的输入用 query string 的形式放到 REST 服务的 URL template 中。之所以叫 template,是因为不同的输入会对应不同的 URL 示例,也就是说对应到不同的资源示例。
  • 知道了两种服务间的逻辑关系,接下来,我们开始用程序把 SOAP 服务转化成 REST 服务,当然,如果系统需要,你也可以把 REST 服务转成 SOAP 服务。

    SOAP Web 服务和 REST 服务的转换

    很多种方式,可以把 SOAP 服务转化成 REST 服务。最直接的方式,程序员可以自己写程序,实现一个 proxy,提供 REST 端点,然后通过 proxy 把 REST 请求转发到 SOAP 端点,然后再实现调用结果的处理。这里我们主要介绍用 IBM 的一些产品来实现转化的方法。IBM WebSphere sMash 和 IBM Mashuphub 都可以实现这种转化。这里着重介绍用 IBM WebSphere sMash 平台实现的方法。使用 IBM Mashuphub 的实现方式请参考 IBM Mashup Center 初探 : 第二部分。

    开始之前

  • 了解 WebSphere sMash
  • WebSphere sMash 即以前的 Project Zero, 它为快速简便地开发交互式 Web 应用程序提供了开发环境。这个项目的目的是为 Web 开发提供一个完整的基础设施,让应用程序开发人员可以将注意力集中在业务逻辑上。花一些时间浏览和熟悉  Project Zero Web 站点。可以加入 Project Zero 社区、为这个项目做贡献,或参与论坛,在各个开发阶段对项目进行评价。本文只要求您的计算机上安装了合适的 Java™ Development Kit (JDK)。

  • 创建 WebSphere sMash 开发环境
  • 遵循下面的步骤,创建 WebSphere sMash 开发环境。

    Step 1:从 WebSphere sMash 网站下载工具包 zero.zip。

    Step 2: 解压 zero.zip 到任意文件夹。如图 1 所示。

    图 1. 解压 zero.zip 到任意文件夹

    Step3: 设置环境变量 ZERO_HOME 和 Path。如图 2 所示。

    图 2. 设置环境变量
    图 2. 设置环境变量

    Step4: 在命令行运行 zero resolve 命令。如图 3 所示。

    图 3. 命令行运行 zero resolve 命令

    Step5: 创建 eclipse 开发环境,详细步骤请参考 Plug-in for Java and Groovy。

    Step6: 创建一个 WebSphere sMash 新项目,取名 SOAP2REST。如图 4 所示。

    图 4. 创建 zero 项目 SOAP2REST

    开始转换

    sMash 提供了一个组件叫 zero.connection.soap.REST2SOAPHandler,它封装了 SOAP 协议,负责构建 SOAP header,发送 SOAP 请求,调用成功后,它返回 SOAP body。所以程序员只需要做其中很小的一部分工作就可以完成 SOAP 到 REST 的转换。按照下面的步骤完成转化的过程。

    Step 1: 在 zero.config 文件里面添加如清单 7 所示的代码片段,配置转换 hanlder 以及转换的对应关系。

    清单 7. 在 zero.config 中申明 REST2SOAPHandler
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 /config/connection/destinations += {  "http://localhost:9980/cms/*": {  "handlers" : [  { "class" : "zero.core.connection.handlers.logger.SimpleJavaLoggerHandler.class" },  { "class" : "zero.connection.soap.REST2SOAPHandler",  "config" : {    "endpointAddress" : "http://www.webservicex.net/stockquote.asmx",    "SOAPVersion" : "1.1",    "r2sMapping" : [      {       "RESTOperation" : "POST",       "SOAPBodyTemplate" : "stockquoterequest.gt",        "URLMatch" : "/cms/stockquote/{symbol}", "SOAPAction": "http://www.webserviceX.NET/GetQuote"         }                       ]           }         }, { "class" : "zero.core.connection.handlers.logger.SimpleJavaLoggerHandler.class",     "config" : { "request" : { "keys" : ["/connection/request/body", "/connection/request/soapHeaders"] }, "response" : { "keys" : ["/connection/response/body",  "/connection/response/soapHeaders"] }         }       }     ]   }  }

    在清单 7 中,配置 REST2SOAPHandler 的各种参数,比如 endpointAddress,SOAPVersion,r2sMapping,实现类等。在 r2sMapping 中,配置 SOAP 和 REST 的对应关系,SOAP 操作由 SOAPAction 属性指定,相对应的 REST 的操作属性由 RESTOperation 指定;另外需要指定的是 SOAPBodyTemplate,用 gt 格式的文件指定;URLMatch 表明了 SOAP 服务端点和 REST 服务端点的对应。在清单 8 给出了 stockquoterequest.gt 的内容。

    清单 8. 指明 SOAP 请求 Header 中的内容
    1 2 3 4 5 6 <% // the SOAP request body %> <GetQuote xmlns="http://www.webserviceX.NET/">      <symbol><%=r2s_getParam("symbol")%></symbol> </GetQuote>

    其中 r2s_getParam("symbol") 指的是从 REST 请求的 request 里面取出来参数的值。比如 REST 请求是 http://localhost:9980/stockquote.gt?symbol=IBM,那么 r2s_getParam("symbol") 的值就是 IBM.

    指明了 handler 和 SOAP 请求后,我们需要创建一个 public 的 zero resource,在 public 目录下面,我们把这个 resource 叫做 stockquote.gt 吧,清单 9 给出了具体的内容。

    清单 9. 声明一个 zero 的 public 的资源 stockquote.gt
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 <% import zero.core.connection.*; def symbol = java.net.URLEncoder.encode(request.params.symbol[]) logger.INFO{symbol} %> <% try { // 这里的 URL 应该和 zero.config 中的 URL 对应,并指明 REST 的操作为 POST conn = new Connection("http://localhost:9980/cms/stockquote/${symbol}",   Connection.Operation.POST); // 指明 content-type conn.addRequestHeader("Content-Type", "application/xml"); // 发送请求 resp = conn.getResponse(); body = resp.getResponseBodyAsString(); if (body == null) { throw new Exception("Response body incorrect: " + body.toString()); } // 取出服务返回的相应 def respObj = zero.json.Json.decode(body); request.json.output = respObj; // 指明一个 gt 用来处理返回的相应,如清单 10 request.view = "stockquote.gt"; render() } catch (Exception e) { e.printStackTrace(); print("<p><strong>Test failed!</strong></p><p> "+zero.util.XMLEncoder.escapeXML(e.toString())+"</p>"); } %>

    声明了 public 的资源后,用户就可以用 http://localhost:9980/stockquote.gt?symbol=IBM 的方式访问资源了。

    清单 10. 处理返回的相应的 groovy 模板
    1 2 3 4 5 6 <% headers.out."Content-Type" = "application/xml" def respObj = request.json.output[] def stockquote = respObj.GetQuoteResponse.GetQuoteResult print(stockquote); %>

    结束语

    本文作为 REST 服务最佳实践的第三篇,通过一个实际的例子,从两种不同类型的 Web 服务的描述入手,辅助于两种不同技术实现的 Web 服务的调用实例,详细介绍 SOAP Web 服务和 REST 服务的关系,并示例介绍基于 WebSphere sMash 的 SOAP Web 服务和 REST 服务的转换,从而使程序员可以轻松的利用已有系统的功能,快速构建 REST 服务。

    总结

    以上是生活随笔为你收集整理的把 SOAP 服务转化为 REST 服务(REST Service 的最佳实践,第 3 部分)的全部内容,希望文章能够帮你解决所遇到的问题。

    如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。