把 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 服务的描述,以及已经会调用他们。接下来,我们来看一下这两种服务的逻辑关系。
知道了两种服务间的逻辑关系,接下来,我们开始用程序把 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 即以前的 Project Zero, 它为快速简便地开发交互式 Web 应用程序提供了开发环境。这个项目的目的是为 Web 开发提供一个完整的基础设施,让应用程序开发人员可以将注意力集中在业务逻辑上。花一些时间浏览和熟悉 Project Zero Web 站点。可以加入 Project Zero 社区、为这个项目做贡献,或参与论坛,在各个开发阶段对项目进行评价。本文只要求您的计算机上安装了合适的 Java™ Development Kit (JDK)。
遵循下面的步骤,创建 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 部分)的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: REST service 化一个数据系统
- 下一篇: REST,Web 服务,REST-ful