使用 Enterprise Service Bus 公开 REST 式服务

相同业务逻辑满足更多客户类型

随着 REpresentational State Transfer (REST) 越来越流行,非 REST 式服务的客户和提供者必须进行调整,以利用 REST 的调用方式。在调整的过程中,Enterprise Service Bus 可以为公开非 REST 式服务提供必要的中介功能,从而实现以 REST 的方式调用这些服务,并且不需要对它们进行任何更改。本文演示如何使用 IBM WebSphere Enterprise Service Bus、IBM WebSphere Message Broker 和 IBM WebSphere DataPower 以 REST 的方式公开服务,并且提供了一些演示例子,以及使用不同编程平台的样例客户。

Ahmed Abbas, IT 架构师, EMC

Ahmed AbbasAhmed Abbas 是 Cairo Technology Development Center 的一名 IT 架构师。在其实验室的工作中,Ahmed 关注于缩小底层技术理论和实现中实际问题之间的差距,以更好地服务于用户业务需求。



Hany Harraz, WebSphere 顾问, WSO2 Inc

http://www.ibm.com/developerworks/i/p-hharraz.jpgHany Harraz 是埃及开罗实验室的 ISSW Tech Practice 团队的 WebSphere 顾问,他的专长是 WebSphere Transformation Extender、DataPower 和 WebSphere Message Broker。在此之前,他使用多种 Microsoft 技术(主要是 C#)开发定制解决方案。他拥有 Cairo University 的计算机工程学士学位。在空闲时间,他喜欢旅游和潜水。



Mohab El-Hilaly, 软件工程师, EMC

http://www.ibm.com/developerworks/i/p-aabbass.jpgMohab El-Hilaly 在位于埃及的 IBM Cairo Technology Development Center 担任软件工程师,他在那里使用 Websphere Enterprise Service Bus 技术为各种客户开发和设计自定义解决方案。在此之前,他使用各种 Microsoft 技术(主要是 ASP.NET)开发自定义解决方案。他从开罗的美国大学获得了计算机科学学士学位。他在业余时间喜欢观看足球赛和玩游戏机游戏。



2010 年 3 月 31 日

简介

作为战略性架构组件的 Enterprise Service Bus 为服务提供者提供了许多选项,帮助他们增强服务的可消费性。这些选项允许将现有逻辑作为服务重用,同时提供了新的客户类别。具像状态传输(Representational state transfer,REST)利用 HTTP 协议提供的经典交互功能。WebSphere Enterprise Service Bus、WebSphere Message Broker 和 WebSphere DataPower 都能够处理 HTTP 请求。因此,它们都能够帮助您公开 REST 式服务。


REST 式服务的特征

简单来说,REST 式服务必须能够作为可通过网络寻址的资源访问,该资源接受服务实现者定义的一组动词。这些动词通常映射到 HTTP 方法(例如 GET、POST、PUT 和 DELETE)。这种映射是常见的使用方式,但不是必要的。下面的流程图显示了一个典型的 REST 交互,其中客户端调用一个 REST 式服务,它向该服务传递需要处理的动词和一些内容(比如参数)。

图 1. 典型的 REST 式交互
典型的 REST 式交互

来自服务的回复可以使用不同的表示法。XML 是这种表示法之一,但还可以是其他对服务提供者和客户有意义的表示法。例如,如果您的服务与一个 Asynchronous JavaScript and XML (AJAX) 应用程序交互,那么使用 JavaScript Object Notation (JSON) 就比使用 XML 要好。另一方面,本文后面提供的例子使用原始的二进制格式在客户端和服务之间交换数据,这进一步强调了表示格式的随意性。


相关的 Web 2.0 技术

还有其他一些 Web 2.0 技术,它们通过丰富 REST 交互来简化该实现。最重要的两种 Web 2.0 技术是 JavaScript Object Notation “JSON”(可用于定义服务返回的对象的格式),和 Asynchronous JavaScript and XML “AJAX”(利用一组现有的技术来定义从客户端到 REST 式服务的数据交换的方式)。

因为本文描述的例子显示了原始数据的交换,所以交互 JSON 格式的数据或从 Ajax 客户端调用服务应该是该例子的特殊情况。


Enterprise Service Bus 的角色

Enterprise Service Bus 的中介功能为服务提供者和客户都提供了更多的选项。最关键的功能之一是改变调用服务的方式,但又不需要更改服务本身。例如,通过使用 IBM Enterprise Service Bus 产品,您可以让 REST 客户端访问现有的基于 SOAP 的 Web 服务(本文的场景 #1)。您还可以利用 Enterprise Service Bus 功能将 REST 式服务公开为基于 SOAP 的服务,以让 Web 服务客户端可以访问它(本文的场景 #2)。下图显示了本文讨论的两个场景,主要关注在 Enterprise Service Bus 上部署的中介功能,它充当现有资产的桥梁:实现业务逻辑的服务或服务组件。

图 2. 可以公开 REST 式服务的 ESB
可以公开 REST 式服务的 ESB

从业务的角度看,如果现有的服务公开了客户所需的所有操作,那么仅需实现通过另一种方式(例如 REST)调用它,而不需要更改该服务,尽管它最初是为基于 SOAP 的客户端编写的。您所需要做的就是生产正确的中介工件,并让客户能够通过 ESB 访问它。

本文的后面小节将展示如何使用 WebSphere ESB、WebSphere Message Broker 和 WebSphere DataPower 为两个场景实现这种中介工件。然后,您将使用一些非常有用的工具来测试所创建的工件。此外,您还将看到一些使用不同的编程平台实现的样例客户:Java、.NET 和 PHP。


实现中介

我们看看如何使用 WebSphere ESB、WebSphere Message Broker 和 WebSphere DataPower 为以上提到的两个场景实现所需的中介工件。

样例后台服务

为了便于显示实现步骤,我们假设存在两个服务:其中一个是使用 SOAP/HTTP 调用的 Web 服务,用于场景 #1,另一个是 REST 式服务,用于场景 #2。这两个访问仅提供一个简单的操作,它接受帐号作为输入参数,并根据业务逻辑规则返回该帐号的信用记录。以下 WSDL 清单显示了在场景 #1 中使用的 Web 服务的界面。

清单 1. 场景 #1 的样例后台服务的 WSDL
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions name="AccountScore"
targetNamespace="http://www.example.org/AccountScore/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://www.example.org/AccountScore/" 
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <wsdl:types>
    <xsd:schema targetNamespace="http://www.example.org/AccountScore/">
      <xsd:element name="getAccountScore">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="accountNumber" type="xsd:string"/>
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
      <xsd:element name="getAccountScoreResponse">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="score" type="xsd:string"/>
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
    </xsd:schema>
  </wsdl:types>
  <wsdl:message name="getAccountScoreRequest">
    <wsdl:part element="tns:getAccountScore" name="parameters"/>
  </wsdl:message>
  <wsdl:message name="getAccountScoreResponse">
    <wsdl:part element="tns:getAccountScoreResponse" name="parameters"/>
  </wsdl:message>
  <wsdl:portType name="AccountScore">
    <wsdl:operation name="getAccountScore">
      <wsdl:input message="tns:getAccountScoreRequest"/>
      <wsdl:output message="tns:getAccountScoreResponse"/>
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="AccountScoreSOAP" type="tns:AccountScore">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="getAccountScore">
      <soap:operation soapAction="http://www.example.org/AccountScore/NewOperation"/>
      <wsdl:input>
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output>
        <soap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="AccountScore">
    <wsdl:port binding="tns:AccountScoreSOAP" name="AccountScoreSOAP">
      <soap:address location=
        "https://localhost:9446/AccountScore/services/AccountScoreSOAP"/>
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

在场景 #2 中使用的 REST 式服务是一个简单的 servlet,清单 2 显示了它的 doPost() 方法。

清单 2. 场景 # 2 的样例后台服务的 doPost() 方法
BufferedReader br = new BufferedReader(new InputStreamReader(request .getInputStream()));
String inputString=br.readLine();
System.out.println(inputString);
String outputString="";

if(inputString.equals("AccountNum=123"))
       	 outputString="Score=4000";
else if(inputString.equals("AccountNum=456"))
       	 outputString="Score=6000";
else
       	 outputString="Score=N/A";

OutputStream os = response.getOutputStream();
byte[] buffer = new byte[outputString.getBytes().length];
response.setContentType(request.getContentType());
buffer=outputString.getBytes();
os.write(buffer, 0, buffer.length);
os.flush();
os.close();

要调用这个 servlet,您仅需向部署该 servlet 的 URL 发送一个 POST HTTP 请求,请求体的内容包含文本 “AccountNum=123” 或 “AccountNum=456”。

注意,为了保持简单,本文讨论的例子仅处理一个动词 (POST)。 您可以通过支持几个动词来让接受前台更加复杂。

这个小节的其余部分将介绍为这两个服务实现中介工件的步骤,以实现本文讨论的两个场景。


使用 WebSphere Enterprise Service Bus

因为 REST 式服务利用 HTTP 协议的功能,所以 WebSphere Enterprise Service Bus 支持使用常规 HTTP 绑定的 REST 交互。本小节的剩余部分解释在 WebSphere Enterprise Service Bus 上实现场景 #1 和 #2 的构思。

理解场景 #1

要实现场景 #1,我们使用一个 HTTP 绑定导出,以接受来自 REST 客户端的 HTTP 请求。需要提醒的是,您可以配置 HTTP 导出以使用预打包函数选择器之一,或者开发定制的选择器,以根据请求消息确定执行哪个操作。在我们的例子中,后台服务仅提供一个操作。因此,不需要任何函数选择器。为此,请参考 WebSphere Integration Developer 信息中心 中的 “Function selectors in export bindings”。

下图显示了 HTTP 导出连接到一个中介流中,这个中介流执行来自 REST 客户端的负载(HTTP 请求体)的转换。如前所述,在本文的例子中我们用二进制格式在负载中发送数据。相应地,中介流将二进制负载的内容转换成字符串格式,它是构成传递到后台服务的 SOAP 请求的 XML 部分。

图 3. 为 Web 服务提供 REST 的界面
可以公开 REST 式服务的 ESB

该中介流可以调用不同类型的后台服务。不过,为了实现场景 #1,中介流将使用一个带有 Web 服务绑定的导入调用样例后台 Web 服务。

实现场景 #1

这个小节演示使用 WebSphere Integration Developer 实现场景 #1 所需的步骤。为了让 Mediation 模块能够将 HTTP 请求和响应作为二进制数据处理,在实现之前需要完成几个步骤。

  1. 确保您当前处于 Business Integration 透视图。从 Business Integration 视图创建一个中介模块,然后右键单击它。从上下文菜单选择 Open Dependencies。如下所示。
图 4. 从上下文菜单选择 “Open Dependencies”
从上下文菜单选择 Open Dependencies
  1. Predefined Resources 下面选择 Schema for predefined HTTP bytes data binding。保存工作并关闭依赖项编辑器。
图 5. 选择 “Schema for predefined HTTP bytes data binding”
选择 Schema for predefined HTTP bytes data binding
  1. Data Types 部分查看是否创建了两个新的数据类型,如下所示。
图 6. 创建的两个数据类型
创建的两个数据类型
  1. 接下来,创建一个用来公开我们刚才创建的中介模块的新界面。界面的输入和输出的类型必须为 HTTPBytes(前面小节创建的数据类型之一),这样才能将负载作为二进制数据处理。
图 7. 中介模块界面的输入和输出应该接受二进制数据
中介模块界面的输入和输出应该接受二进制数据

现在,您已经创建了一个可以接受二进制数据的界面,您可以通过以下步骤将它与一个 Export 结合使用,以通过 HTTP 公开该模块。

  1. Assembly Editor 中创建一个新的 Export,并将前面创建的界面其拖放到其中。
图 8. 创建一个包含先前创建的界面的新导出
创建一个包含先前创建的界面的新导出
  1. Assembly Editor 上右键单击 Export。从上下文菜单选择 Generate Binding > HTTP Binding
图 9. 为导出生成 HTTP 绑定
为导出生成 HTTP 绑定
  1. 输入 Context path,它是表示 REST 式服务的中介模块的 URL 的一部分的。例如,REST 式服务的 URL 可以为 http://your_server_ip:your_server_port/RestIntegration_MediationModuleWeb/Export1
图 10. 设置导出的 “Context path”
设置导出的 Context path
  1. 将导出连接到中介流。
图 11. 将导出连接到中介流
将导出连接到中介流

至此,中介模块已经可以通过 HTTP 接受二进制数据。下面的步骤将引导您如何让中模块调用基于 SOAP 的后台 Web 服务,以完成场景 #1。

  1. Assembly Editor 中,将用于场景 #1 的样例 Web Service 的 WSDL 文件拖放到画布上,并选择 Import with Web Service Binding。这个步骤将根据样例 WSDL 为您创建一个新的导出。
图 12. 选择 “Import with Web Service Binding”
选择 Import with Web Service Binding
  1. 将中介流原语连接到新创建的导出。
图 13. 将中介流连接到导出
将中介流连接到导出
  1. 双击中介流将您为 REST 客户提供的操作映射到后台 Web 服务提供的操作。
图 14. 在导出和导入界面之间映射操作
在导出和导入界面之间映射操作
  1. 在画布上创建一个新的 Business Object Mapper 原语,这个映射器的目的是将二进制输入转换成字符串并操作它。它仅将来自输出的请求操作 getScore 的消息转换成来自输入的请求操作 getAccountScore。完成该映射的其他方式是使用定制中介或 XSLT 原语。
图 15. 创建一个 Business Object Mapper,用于保存导出和导入之间的映射
创建一个 Business Object Mapper,用于保存导出和导入之间的映射
  1. 创建一个定制映射,以在前一个步骤中创建的 Business Object Mapper 中执行映射。
图 16. 为请求创建映射
为请求创建映射
清单 3. 处理请求的定制映射
String inputString= new String((byte[])ServiceMessageObject_body_getScore_input1_value);
String accNum=inputString.split("=")[1];
ServiceMessageObject_1_body_getAccountScore_accountNumber=accNum;
  1. 在响应路径上创建一个类似的映射,以将来自 Web 服务的响应字符串转换成二进制格式。
图 17. 为响应创建映射
为响应创建映射
清单 4. 处理响应的定制映射
String Score="Score=";
Score+=ServiceMessageObject_body_getAccountScoreResponse_score;
ServiceMessageObject_1_body_getScoreResponse_output1_value=Score.getBytes();

至此,您已经拥有一个与前面提过的样例 Web 服务等效的 REST 中介。这个中介可以通过在导出(步骤 7)中指定的 URL 访问并接受 POST HTTP 操作。该服务还执行另一个操作,即检查作为 HTTP 请求的参数传递的帐号的信用得分,其格式为 “AccountNum=XYZ”。响应是一个二进制字符串,它将与帐号容器对应的信用得分表示为参数。

理解场景 #2

场景 #2 的实现在工件和使用方面类似于前面的场景 #1,如下图所示。

图 18. 为 REST 式服务提供一个 Web 服务界面
为 REST 式服务提供一个 Web 服务界面

唯一的区别是导出使用 Web 服务绑定,导入使用 HTTP 绑定,中介流从 SOAP 请求提取负载并将其作为二进制负载发送到样例后台 REST 式服务。

实现场景 #2

下面的步骤展示如何使用直接模块将基于 SOAP 的客户端和使用 WebSphere Integration Developer 的 REST 后台服务连接起来。

  1. 接下来将创建一个与导出一起使用的新界面,用于将中介模块公开为 Web 服务。
图 19. 创建和导出一起使用的界面
创建和导出一起使用的界面
  1. 将该界面拖放到 Assembly Editor 的画布上并选择 Export with Web Service Binding
图 20. 基于界面创建导出
基于界面创建导出
  1. 使用实现场景 #1 时在步骤 1 至 4 中创建的界面。这次,您将它和导出一起使用,以调用用于场景 #2 中的样例 REST 后台服务。将该界面拖放到画布上并选择 Import with no Binding。您将在下一个步骤中定义绑定。
图 21. 创建导入
创建导入
  1. 右键单击创建的导入并选择 Generate Binding > HTTP Binding
图 22. 为导入创建 HTTP 绑定
为导入创建 HTTP 绑定
  1. 将 URL 指定为 REST 提供者的地址。在我们的例子中使用了简单的 servlet,它的 doPost() 方法已在本文的前面提供。
图 23. 将端点 URL 设置为 REST 式服务的地址
将端点 URL 设置为 REST 式服务的地址
  1. Mediation flowImportExport 连接起来,然后就可以实现中介流本身了。
图 24. 将导入和导出连接到中介流
将导入和导出连接到中介流

至此,您已经拥有一个与前面提供的样例 REST 式服务等效的 Web 服务中介。这个中介的功能与场景 #1 中的中介的功能相同,但使用的是 SOAP 界面。


使用 WebSphere Message Broker

WebSphere Message Broker 支持在一个消息流中使用 3 个原语进行 REST 交互:HTTPInput、HTTPRelpy 和 HTTPRequest。本小节的剩余部分将解释在 WebSphere Message Broker 上实现场景 #1 和 #2 的构思。

理解场景 #1

在 WebSphere Message Broker 上实现场景 #1 和在 WebSphere Enterprise Service Bus 实现它使用的概念差别不大。可以使用一个 HTTPInput 节点从 REST 客户端接受 HTTP 请求,如下图所示。

图 25. 为 Web 服务提供 REST 界面
为 Web 服务提供 REST 界面

HTTPInput 将请求转发到一个 Compute 节点,该节点为未来处理需求将请求负载从二进制转换成字符串类型。Compute 节点通过 SOAPRequest 节点为场景 #1 调用样例后台 Web 服务以处理基于 SOAP 的通信。HTTPReply 节点构造 HTTP 响应并将其发送回到请求 REST 客户端。

实现场景 #1

这个小节显示使用 WebSphere Message Brokers Toolkit 实现场景 #1 的步骤。

  1. 创建一个 Message Flow,然后将 HTTPInput 节点拖放到画布并在 HTTPInput 字段输入 Path suffix for URL,它是表示 REST 式服务的流的 URL 地址的一部分。访问输入节点的请求地址类似于 http://localhost:7080/Rest
图 26. 设置 “Path suffix for URL”
设置 Path suffix for URL
  1. 确保消息类型为 Binary Large Object “BLOB”,以阻止节点从 REST 客户端解析进入消息的负载。然后,负载被按原样(二进制数据)传递给稍后将创建的计算节点。
图 27. HTTPInput 消息域为 BLOB
HTTPInput 消息域为 BLOB
  1. 现在我们处理调用样例后台 Web 服务的部分。将场景 #1 中的样例后台 Web 服务的导入 WSDL 文件拖放到画布上。这将创建一个 Service Invocation 子流。为了验证创建是否成功,双击子流调用原语时它应该可用。
图 28. Service Invocation 子流
Service Invocation 子流
  1. Service Invocation 将消息的格式在 BLOB 和 SOAP 之间转换之前和之后添加一个计算节点。然后,放置一个 HTTPReply 节点返回请求的响应。
图 29. 完整的消息流
完整的消息流
  1. 要添加执行转换的代码,在 Service Invocation 之前双击 Compute 节点。为此,可以使用下面的代码片段。
清单 5. 将二进制负载转换成 XML 的代码
DECLARE InputString CHARACTER;
DECLARE OutString CHARACTER;
DECLARE CCNum CHARACTER;
SET InputString = CAST ( InputRoot.BLOB.BLOB AS CHAR CCSID 1208);
		
SET OutString = OVERLAY(InputString PLACING '' FROM 1 FOR POSITION('=' IN InputString));

SET OutputRoot.SOAP.Body.ns:getAccountScore.accountNumber=OutString;
RETURN TRUE;
  1. 在 Service Invocation 之后添加 Compute 节点的代码;如清单 5 所示。
清单 6. 将 SOAP 转换成二进制的代码
DECLARE OutString CHARACTER 'Score=';
Set OutString=  OutString || InputRoot.XMLNSC.ns:getAccountScoreResponse.score;
SET OutputRoot.BLOB.BLOB=CAST (OutString AS BLOB CCSID 1208);
RETURN TRUE;

完成以上步骤之后,您将拥有一个与前面创建的 REST 中介(在 WebSphere Enterprise Service Bus 上实现场景 #1 时创建)的行为相同的 REST 中介。新的 REST 服务的 URL 地址是您在以上的步骤 1 指定的地址。

了解场景 #2

WebSphere Message Broker 提供 SOAPInput 节点来接受来自 Web 服务客户端的基于 SOAP 的请求。下面的图显示该请求接着被转发到一个 Compute 节点,以将进入的请求从 SOAP 转换成二进制。

图 30. 为 REST 式服务提供一个 Web 服务界面
为 REST 式服务提供一个 Web 服务界面

二进制消息用于调用本文前面提到的样例 REST 式服务,现在,将通过一个 HTTPRequest 节点在场景 #2 中使用该服务。然后,使用一个 Compute 节点将结果转换回到 SOAP,SOAP 可以通过一个 SOAPReply 节点发送回到请求 Web 服务客户端。

实现场景 #2

这个小节显示使用 WebSphere Message Broker Toolkit 实现场景 #2 的步骤。

  1. 将流作为 Web 服务公开,将样例 Web 服务的 WSDL 文件导入到一个新的消息集中。将 WSDL 拖放到画布上,以准备好在下一个步骤中使用它。
图 31. 导入样例服务的 WSDL 文件
导入样例服务的 WSDL 文件
  1. 放置一个 Compute 节点,将进入的消息从 SOAP 转换成 BLOB(二进制)以便发送回到 REST 后端服务。
清单 7. 将 SOAP 转换成二进制的代码
DECLARE OutString CHARACTER 'AccountNum=';
Set OutString=  OutString || InputRoot.SOAP.Body.ns:getAccountScore.accountNumber;
SET OutputRoot.BLOB.BLOB=CAST (OutString AS BLOB CCSID 1208);
RETURN TRUE;
清单 8. 将二进制转换成 SOAP 的代码
DECLARE InputString CHARACTER;
DECLARE OutString CHARACTER;
SET InputString = CAST ( InputRoot.BLOB.BLOB AS CHAR CCSID 1208);
SET OutString = OVERLAY(InputString PLACING '' FROM 1 FOR POSITION('=' IN InputString));
Set OutputRoot.XMLNSC.ns:getAccountScoreResponse.score=OutString;
RETURN TRUE;
图 32. 添加一个将 SOAP 转换成 BLOB 的计算节点
添加一个将 SOAP 转换成 BLOB 的计算节点
  1. 放置一个将调用样例后台 REST 式服务的 HTTPRequest 节点。
图 33. 添加一个 HTTPRequest 节点
添加一个 HTTPRequest 节点
  1. 对于 HTTPRequest 节点,输入 REST 式服务的 URL 地址。在我们的例子中,我们使用其 doPost() 方法已经在本文前面提供的简单 servlet。
图 34. 指定后台服务端点
指定后台服务端点
  1. 最后,放置一个 Compute 节点,将来自后台 REST 式服务的响应转换成 SOAP 响应,然后通过一个您添加的 SOAPReply 节点将该 SOAP 响应发送到调用 Web 服务客户端。
图 35. 添加一个 Compute 节点和一个 SOAPReply 节点
添加一个 Compute 节点和一个 SOAPReply 节点

至此,您已经拥有一个 Web 服务,其行为与前面在实现场景 #2 时使用 WebSphere Enterprise Service Bus 创建的 Web 服务完全一样。


使用 WebSphere DataPower

WebSphere DataPower 以多种方式支持 REST 交互。这个小节解释在 WebSphere DataPower 上实现场景 #1 和 #2 的一种方式。

理解场景 #1

可以通过几种方式实现场景 #1。现在我们推荐的方式将使用 XML Firewall 从 REST 客户端接受 HTTP 请求,如下所示。

图 36. 为 Web 服务提供一个 REST 接口
为 Web 服务提供一个 REST 接口

由于进入的请求包含二进制负载,所以 XML Firewall 将使用一个 WebSphere Transformation Extender 映射将该二进制负载转换成可被 Web 服务接受的 SOAP。另一个转换刚好相反,它将来自 Web 服务的 SOAP 响应转换成可以发送到请求 REST 客户端的二进制负载。除了这两种转换之外,XML Firewall 还使用 XSLT 函数通过一个 Web Service Proxy 调用后台 Web 服务。因此,在这个场景中我们对 XML Firewall 使用回送选项。这也可以使用一个调用 Web Service Proxy 的静态后台来实现。

对于另一种不需要处理二进制负载的实现,请参考文章: http://www.ibm.com/developerworks/websphere/techjournal/0903_peterson/0903_peterson.html

实现场景 #1

这个小节显示在 WebSphere DataPower 上实现场景 #1 所需的步骤。首先,实现在二进制和 XML 之间转换的映射。然后,配置 Web Service Proxy 以处理与后台Web 服务的通信。最后,使用您刚才使用 XML Firewall 创建的、负责接收 REST 请求的映射将负载从二进制转换成 XML,然后将负载发送到将其传递到样例后台 Web 服务的 Web Service Proxy。

让我们先使用 WebSphere Transformation Extender Design Studio 构建两个 Type Trees,用于定义进入请求消息和发送到样例后台 Web 服务的 SOAP 消息的外观。

对于非 XML 输入,我们将手动地生成类型树,我们希望收到这种形式的输入 AccountID=123。

  1. 为来自 REST 客户端的消息(将看作二进制,也称为非 XML)创建一个新的 Type Tree
图 37. 创建 Type Tree
创建 Type Tree
  1. 在新创建的 Type Tree 下创建一个 Group 和两个 Items,如下所示。
图 38. 创建后代
创建后代
  1. 因为来自 REST 客户端的负载的期望格式为 AccountNum=123,所以将名为 “Property” 的 Item 的终止符设置为 “=”,从而让 WTX 引擎将 ‘=’ 号前面的部分解释为类型 “Property”,并将等于号后面的作为值。
图 39. 定义分隔符
定义分隔符
  1. 选择 Tree > Analyze,然后选择 Save
  2. 为将要发送到样例后台 Web 服务的 SOAP 消息创建另一个 Type Tree。您可以通过导入样例后台 Web 服务的 WSDL 文件来创建该 Type Tree。
图 40. 导入样例后台 Web 服务的 WSDL 文件
导入样例后台 Web 服务的 WSDL 文件
  1. 在按下 Finish 按钮之前,其他选项均使用默认值。打开生成的树,然后按 AnalyzeSave

至此,您已经创建了两个 Map Type Tree (MTT) 文件。在接下来的步骤中,您将发现它们作为描述来自客户端的二进制消息的 Binary.mtt,以及作为基于样例后台 Web 服务的 WSDL 文件描述该消息的 AccountScore.mtt。现在,您已经准备好使用这些 Type Trees 通过以下步骤创建 Map 本身。

  1. 创建一个带有 Input Card 和 Output Card 的映射,如下所示。
图 41. 创建一个新的映射
创建一个新的映射
  1. 将 Input Card “Binary” 的属性设置为下面显示的值,其中 binary.txt 是一个文本文件,包含用于测试的内容 AccountID=123。注意,您现在将 Type Tree 属性设置为您前面为二进制消息创建的 Type Tree 的值。
图 42. 设置 Input Card 的属性
设置 Input Card 的属性
  1. 按照下图为 Output Card “XML_OUT” 设置属性。这次使用前面为基于 SOAP 的 Web 服务消息创建的另一个 Type Tree。
图 43. 设置 Output Card 的属性
设置 Output Card 的属性
  1. 使用下图所示的值完成映射。
图 44. 映射字段
映射字段
  1. 选择 Map > BuildMap > Run 测试映射。
  2. 将映射的 Map SettingsMapRuntime 属性更改为 DataPower(取代 WebSphere Transformation Extender),从而让编译后的映射包含可部署到 DataPower 的扩展名 “.dpa”。例如,您可以调用文件 “BinaryToXML.dpa”。选择 Map > Build 生成该文件。
图 45. 准备部署
准备部署

现在已经完成了所需的所有类型映射,您可以部署在 WebSphere DataPower SOA 工具上创建的工件了。在应用映射之前,您将通过以下步骤在 WebSphere DataPower 上创建一个 Web Service Proxy 和一个 XML Firewall。

  1. Control 面板单击 Web Service Proxy 图标,然后单击 Add。输入 Web Service Proxy 名(例如 WS-BinaryToSOAP)并单击 Create Web Service Proxy 按钮。
  2. 如果还没有加载样例后台 Web 服务的 WSDL,输入 File to upload 并单击 Upload
图 46. 加载 WSDL 文件
加载 WSDL 文件
  1. 在加载了该文件之后,单击 Next
图 47. 完成 Web Service Proxy 配置
完成 Web Service Proxy 配置
  1. 创建一个新的 Front Side Handler。分配一个 IP、端口和支持的动词,然后单击 Add
图 48. 创建一个 Front Side Handler
创建一个 Front Side Handler
  1. 在单击 Next 之前,为后台样例 Web 服务设置 IP, port 和 Remote URI

现在已经配置好 Web Service Proxy,可以调用样例后台服务了。以下步骤显示如何配置一个回送 XML Firewall,它将把进入的请求发送到刚才创建的 Web Service Proxy。

  1. 从 DataPower Control Panel 选择 XML Firewall。然后单击 Add
  2. 选择 Pass Thru,如下所示。然后单击 Next
图 49. 创建回送 XML Firewall
创建回送 XML Firewall
  1. 输入名称,例如 RESTtoSOAPService。
  2. Firewall Type 选择 loopback-proxy
  3. 输入 Device Address 和将接收进入的客户端请求的端口。我们不推荐将 IP 设置为 0.0.0.0。单击 Next
图 50. 设置 XML Firewall 的地址
设置 XML Firewall 的地址
  1. 选择 Commit,然后单击 Done
  2. 打开 XML Firewall 设置并将 Request Type 更改为 Non-XML,因为我们期望从 REST 客户端接收二进制负载。
  3. 打开 Firewall Policy 并编辑它。这需要以下 4 个步骤。
图 51. 编辑 XML Firewall Policy
编辑 XML Firewall Policy
  1. A Match Rule,为指定 IP 地址和端口上的任何 URI 接受所有进入请求。
  2. A Binary Transformation 操作,它调用在前面创建的 WTX 映射之一,并将二进制输入转换成 SOAP 请求,如下所示。
图 52. 配置 Transform Binary 操作
配置 Transform Binary 操作
  1. Transformation 操作使用一个简单的 XSLT 通过 Web Service Proxy 调用样例后台服务,并创建发送回到请求客户端的响应消息。下面显示了这个转换的样式表。
清单 9. 调用 Web Service Proxy 的样式表
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
    xmlns:dp="http://www.datapower.com/extensions"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    extension-element-prefixes="dp"
    exclude-result-prefixes="dp"
    version="1.1">
    
    <xsl:template match="/">
        <xsl:copy-of
select="dp:soap-call('http://the_address_of_wsproxy:the_port
                    _of_wsproxy/mockAccountScoreSOAP',/,'',0)"/>
    </xsl:template>
    
</xsl:stylesheet>
  1. Binary transformation 操作使用一个 XSL 样式表将 SOAP 响应转换成发送到客户端的非 XML 响应。下图显示了这个转换的样式表。
清单 10. 从 SOAP 转换成二进制的样式表
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
                xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:output method="text"/>
<xsl:template match="/">score=<xsl:value-of select="//score"/>
</xsl:template>
</xsl:stylesheet>

通过以上步骤,您已经创建了一个 REST 式服务,它的行为与在 WebSphere Enterprise Service Bus 和 WebSphere Message Broker 上实现场景 #1 创建的 REST 式服务一样。新的 REST 式服务的 URL 地址用于 XML Firewall。

理解场景 #2

对于场景 #2,我们主要交互 Web Service Proxy 和 XML Firewall,如下所示。

图 53. 为 REST 式服务提供一个 Web 服务界面
为 REST 式服务提供一个 Web 服务界面

Web Service Proxy 根据样例后台 Web 服务的 WSDL 才能够从 Web 服务客户端接受 SOAP 请求,然后将该请求传递到 XML Firewall 以在 SOAP 和二进制之间执行所需的转换。XML Firewall 还调用样例后台 REST 式服务。

实现场景 #2

和实现场景 #1 一样,您首先要创建一个工件,它将使用 WebSphere Transformation Extender Design Studio 在基于 XML 的内容和二进制内容之间执行映射。幸运的是,您可以使用为场景 #1 创建的两个类型树。不过,您将以不同的方式使用它们。以下步骤显示了如何创建一个 Response Map,用于将从样例后台 REST 式服务返回的二进制响应映射到 SOAP,从而使它能够通过 Web Service Proxy 发送回到请求 Web 服务客户端。

  1. 遵循在场景 #1 中创建请求映射的步骤创建 Response Map。对于 XML_OUT Output Card,选择为响应选择 Envelope 组,如下图所示。
图 54. 创建 Response Map
创建 Response Map
  1. 如下所示完成映射。您还需要选择 Rules > Insert NONE if Empty 以自动地将空字段设置为 NONE。
图 55. 映射字段
映射字段
  1. 选择 Map > Build 和 Map > Run 测试映射。
  2. 在 Map Settings 上将 MapRuntime 更改为 WebSphere DataPower ,这和场景 #1 一样。

至此,已经可以对 WebSphere DataPower 使用映射工件。以下步骤显示了如何配置 WebSphere DataPower。

遵循场景 #1 中的步骤创建 Web Service Proxy。不过,您不是将后台设置为 Web 服务,而是将其设置为下面将创建的 XML Firewall 的 IP 地址和端口。这个 XML Firewall 将 SOAP 请求/响应转换成二进制使其能够与样例后台 REST 式服务通信。

您还需要创建一个 XML Firewall,以下步骤显示了细节。

  1. 创建一个带有静态后台的新 XML Firewall(而不是在场景 #1 中使用的回送)。输入驻留样例后台 REST 式服务的服务器的 Server Address 和 Port。
图 56. 创建一个 Pass Thru XML Firewall
创建一个 Pass Thru XML Firewall
  1. 打开新创建的 XML Firewall 并为前台将 Request Type 设置为 SOAP,为后台将 Request Type 设置为 Non-XML。
  2. 打开 Processing Policy 创建一个 Request Rule 和一个 Response Rule,如下所示。
    1. 为 Request Rule 定义以下操作。
    2. 对于 Response Rule,仅创建一个 Binary Transformation 操作,它使用您创建的 WebSphere Transformation Extender Response Map 将二进制响应转换成 SOAP 响应,这个 SOAP 响应将通过 Web Service Proxy 代理发送到请求 Web 服务客户端。
图 57. 为 Request 编辑 XML Firewall Policy
为 Request 编辑 XML Firewall Policy
清单 11. 示例
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
            xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:output method="text"/>
<xsl:template match="/">AccountID=<xsl:value-of select="//accountNumber"/>
</xsl:template>
</xsl:stylesheet>
图 58. 为 Response 编辑 XML Firewall Policy
为 Response 编辑 XML Firewall Policy

至此,您已经拥有一个 Web 服务,它的行为与前面实现场景 #2 时使用 WebSphere Enterprise Service Bus 和 WebSphere Message Broker 创建的 Web 服务一样。


测试服务提供者

现在您已经为服务构建了不同的界面,一些是基于 SOAP 的,一些是基于 REST 的。接下来,您将使用一些样例工具确保这些服务是正常运行的。虽然这些工具还不是很全面,但是它们能够出色的测试您创建的中介和后台服务。下面概述了我们讨论的工具,并描述如何适当地使用它们根据您实现的两个场景测试服务提供者。

图 59. 使用工具测试服务提供者
使用工具测试服务提供者

TCP/IP Monitor

TCP/IP Monitor 是随 WebSphere Integration Developer、几个其他 Rational Software Delivery 产品和底层 Eclipse 平台附带的有用实用程序。TCP/IP Monitor 允许您监控客户端和应用服务器之间的流量。您可以使用 TCP/IP Monitor 查看 REST 式服务的出入流量和您在本文中创建的 Web 服务。下面给出了有关示例,演示了使用 WebSphere Integration Developer 监控 REST 客户端和服务提供者之间的流量的步骤。注意,服务提供者可以是样例 REST 后台服务(即用于场景 #2 的 servlet),或者是您为场景 #1 包装服务而创建的中介。

  1. 单击 Window > Show view > TCP/IP Monitor
图 60. TCP/IP Monitor 视图
TCP/IP Monitor 视图
  1. TCP/IP Monitor 视图内右键单击,然后选择 Properties
图 61. 显示监控器的属性
显示监控器的属性
  1. 添加一个 TCP/IP 来监控服务器并更改服务器端口以匹配测试服务的环境。
图 62. 配置监控服务器
配置监控服务器
  1. 配置完成之后,您应该能够监控服务的流量。从任意 REST 客户端向 REST 式服务发送一个请求。注意,您要将请求发送到部署 REST 式服务的端口,而不是 TCP/IP 监控服务器本身。例如,如果您在 9090 上部署了 REST 后台服务,那么让客户端将请求发送到该端口,而不是您创建的监控器的端口(上图所示的 9083)。如果您还没有 REST 客户端也不必担心,因为您还有一个用于该目的的工具,以及一些用于测试服务的样例客户端代码。
  2. 将请求发送到服务时,您将在 TCP/IP Monitor 视图中看见它的内容,该视图显示了来自客户端的请求和来自中介模块的响应。
图 63. 显示请求和响应的内容
显示请求和响应的内容

如本小节前面所述,您也可以使用 TCP/IP Monitor 来监控 Web 服务,步骤同上。


cURL

cURL 是一个流行的第三方开源命令行工具,用于将文件和数据传输到一个适合测试您创建的 REST 提供者的 URL。下面是一个样例命令,可用于将请求发送到您在 WebSphere Enterprise Service Bus 上创建的中介流。

curl -H "Content-type: text/plain" -X POST -d "AccountNum=123"  
http://your_server_ip:your_server_port/RestIntegration_Medi
ationModuleWeb/Export1

根据样例 REST 式服务的逻辑,来自服务的响应应该为 Score=4000。您将使用 TCP/IP Monitor 监控该命令导致的在 cURL 和您的服务之间的请求和响应交互,如前一小节所述。

您还可以使用 cURL 将 SOAP 请求发送到您创建的 Web 服务提供者。


RESTClient

RESTClient 是另一个可用于测试 REST 应用程序的第三方开源 Java 应用程序。以下步骤显示如何使用该工具测试 REST 式服务。

  1. 使用以下命令运行 RESTClient:
$ java –jar [path to RESTClient jar]
图 64. 运行 RESTClient 工具
运行 RESTClient 工具
  1. 输入将要测试的 REST 式服务提供者的 URL。例如,您在场景 #1 中在 WebSphere Enterprise Service Bus 上创建的中介流的 URL。
图 65. 输入 REST 式服务的 URL
输入 REST 式服务的 URL
  1. 将 HTTP 方法更改为 POST
图 66. 将 HTTP 方法设置为 POST
将 HTTP 方法设置为 POST
  1. 单击 Body 选项卡并输入 “AccountNum=123”,它是将要发送到 REST 式服务的负载。
图 67. 输入负载
输入负载
  1. URL 旁边按 Send 按钮并观察来自 REST 式服务的响应。
图 68. 查看响应
查看响应

如在 cURL 中提到的一样,您将使用 TCP/IP Monitor 监控从 REST 客户端发送到 REST 式服务提供者的流量。


Web Service Explorer

和 TCP/IP Monitor 一样,Web Service Explorer 也是随 WebSphere Integration Developer 附带并且是 Eclipse 平台的一部分的工具。Web Service Explorer 允许开发人员调用给出了 WSDL 和位置的 Web 服务。以下步骤显示了如何使用 Web Service Explorer 从 WebSphere Integration Developer 内部测试 Web 服务提供者。

  1. 右键单击需要测试的服务的 WSDL 文件。选择 Web Services > Test With Web Service Explorer
图 69. 运行 Web Service Explorer
运行 Web Service Explorer
  1. 如果没有列出 Web 服务的端点,那么使用 Add 链接添加它。
图 70. 列出 Web 服务的端点
列出 Web 服务的端点
  1. 单击将要调用的操作。例如,样例 Web 服务的 getAccountScore() 或为场景 #2 创建的中介。如上图所示。
  2. 选择端点并添加将要发送的 Web 服务的参数。例如 “123”,然后单击 Go 按钮。
图 71. 选择端点并传递参数
选择端点并传递参数
  1. 您应该看到从 Web 服务提供者返回的结果。
图 72. Web 服务的响应
服务的响应

使用 REST 式服务提供者

公开 REST 式服务的好处之一是它们可以被多种客户端编程语言使用,并且不会出现互操作性问题。这是因为 REST 交互基于 HTTP 协议,该协议在不同的软件产品供应商之间非常稳定。下面的代码片段是用 Java、.NET 和 PHP 编写的,用于调用您为场景 #1 创建的 REST 式服务和在场景 #2 中使用的样例后台 REST 式服务。

Java 使用者

基于 Java 的使用者可以是 JavaServer Page (JSP) 或 Servlet 等。下面的清单显示了如何调用您公开的 REST 式服务。

清单 12. 用 Java 代码调用 REST 式服务
//(1)
	URL url = new URL(the_URL_address_of_the_REST_provider);
	HttpURLConnection con = (HttpURLConnection)url.openConnection();
			
	//(2)
	con.setRequestMethod("POST");
		
	//(3)
	con.setRequestProperty("Content-Type", "text/plain");
			
	//4
	String content=”AccountNum=123”
	byte[] contentBytes=content.getBytes();

	//5		    
	OutputStream out = con.getOutputStream();
	out.write(contentBytes);
	out.flush();
	out.close();
		
	//6	    	
	BufferedReader br
			=new BufferedReader(new InputStreamReader(con.getInputStream()));
	String result = br.readLine();
	con.disconnect();

步骤 (1),打开一个到 REST 提供者的连接。步骤 (2) 将 HTTP 方法设置为 POST,它决定由 REST 式服务执行的操作。在步骤 (3) 中设置 HTTP 头部。步骤 (4) 通过将字符串转换成字节序列(比如二进制)准备好通过流发送的请求。请求在步骤 (5) 中发送。步骤 (6) 读取从服务返回的响应。

.NET 使用者

类似地,.NET 使用者可以是基于 Web 或 Windows 的应用程序。经典的 ASP 或现代的 ASP.NET 都是基于 Web 的。

清单 13. 从 C# 调用 REST 式服务
//(1)
	// url is the address of the REST provider 
	HttpWebRequest myReq =(HttpWebRequest)WebRequest.Create((String) url );
	
	//(2)			
	myReq.Method = "POST";
	String content=”AccountNum=123”
	byte[]  contentdata = Encoding.UTF8.GetBytes(content);
	myReq.ContentLength = contentdata.Length   ;			
	myReq.ContentType = "text/plain ;					
	myReq.Credentials = CredentialCache.DefaultCredentials;
	
	//(3)
	Stream strm =  myReq.GetRequestStream();
	strm.Write(contentdata,0,contentdata.Length);    
	strm.Close();
			

	//4		
	HttpWebResponse myres =( HttpWebResponse)myReq.GetResponse();
	Stream recStream = myres.GetResponseStream();
	StreamReader strRead = new StreamReader(recStream, Encoding.UTF8);
	string result = strRead.ReadToEnd();
	strRead.Close();
	myres.Close();

步骤 (1) 打开一个到 REST 提供者的 Web 请求。步骤 (2) 设置 HTTP 请求头,并准备在步骤 (3) 中通过流发送的负载。步骤 (4) 从服务读取响应。

PHP 使用者

用 PHP 编写的使用者如下所示。

清单 14. 从 PHP 代码调用 REST 式服务
//(1)
	$properties = array(
	  'http'=>array(
	    'method'=>"POST"
	    ,'header'=>"Content-Type: text/plain; charset= utf-8\r\n"
	    ,"content"=>"AccountNum=123"
	    )
	    );

	//(2)	    
    	$context = stream_context_create($properties);

	//(3)
	// $url is the address of the REST provider 
	$result = file_get_contents($url, false, $context);

步骤 (1) 设置在客户端和服务器之间创建流所需的属性,服务器的创建在步骤 (2) 完成。步骤 (3) 将请求发送到 REST 式服务并接收返回的结果。


致谢

本文作者衷心感谢 Greg Flurry 审阅了本文并提出宝贵的意见。


结束语

您可以使用 Enterprise Service Bus 将旧有的应用程序公开为 REST 式服务。REST 式服务本身可以使用 Enterprise Service Bus 以不同的方式公开。在本文中,您看到了两个场景。一个场景将后台 Web 服务公开为 REST 式服务,另一个场景将后台 REST 式服务公开为 Web 服务。这两个场景是在 WebSphere Enterprise Service Bus、WebSphere Message Broker 和 WebSphere DataPower 上实现的。您还学习了如何测试 Web 服务和 REST 界面。最后,您还使用不同的编程语言实现使用相同的 REST 式服务的使用者,从而体验到 REST 互操作性的强大之处。

参考资料

学习

获得产品和技术

  • 下载 IBM 产品评估版在线试用 IBM SOA Sandbox,并开始使用来自 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere® 的应用程序开发工具和中间件产品。
  • WebSphere 下载中心,您可以获得最新的 WebSphere 产品的试用版和免费版,让您可以提前免费体验相关产品的强大功能。

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=SOA and web services
ArticleID=478827
ArticleTitle=使用 Enterprise Service Bus 公开 REST 式服务
publish-date=03312010