介绍
REpresentational State Transfer (REST) 是一种架构原则,其中将 web 服务视为资源,可以由其 URL 唯一标识。RESTful Web 服务的关键特点是明确使用 HTTP 方法来表示不同的操作的调用。
REST 的基本设计原则对典型 CRUD 操作使用 HTTP 协议方法:
- POST - 创建资源
- GET - 检索资源
- PUT – 更新资源
- DELETE - 删除资源
REST 服务的主要优势在于:
- 它们是跨平台 (Java、.net、PHP 等)高度可重用的,因为它们都依赖基本 HTTP 协议。
- 它们使用基本的 XML,而不是复杂的 SOAP XML,使用非常方便。
基于 REST 的 web 服务日益成为后端企业服务集成的首选方法。与基于 SOAP 的 web 服务相比,它的编程模型简单,而本机 XML(而不是 SOAP )的使用减少了序列化和反序列化过程的复杂性,并且不再需要其他作用相同的第三方库。
当前用于构建 RESTful 服务,比如 Apache CXF、RESTlet、JAX-WS API 和 REST 支持的基于 Java 的框架可从 Spring 3.0 中获得,它在开发和 XML 配置方面非常复杂,通常需要长期的学习。此外,由于这些框架依赖特定版本的 jar 文件,它们很难跨应用程序服务器环境集成。另外,由于一些同时支持 SOAP 和 REST 服务的尝试(Apache CXF、JAX-WS),它们软件包也往往很大,也可能会影响性能。
因此本文建议使用更简单的可扩展框架将业务服务公开为类 REST 服务。该框架是轻量级的,采用标准的 Front Controller(前端控制器)模式,非常便于理解。它也是可扩展的,可以通过 API 或任何其他集成模式(如 ESB)集成后端服务。通过使用自定义 XML 序列化程序、JAXB 或任何其他对象到 XML 转换工具,可以方便地配置数据交换模型。
本文将详细描述此框架。
架构概述
在 J2EE 应用程序中,Java API 或服务公开为 Stateless Session Bean API(Session Façade 模式) 或 SOAP web 服务。在这些服务与使用非 Java 技术 (如 .net 或 PHP)的客户端应用程序集成时,处理 SOAP Web 服务将变得非常麻烦,还涉及到大量的开发工作。
这里提到的方法通常用于有很多服务、服务可以重复使用,但使用 SOAP 创建快速集成障碍的互操作性和开发成本很大的组织,帮助它们进行服务集成。此外,在内部治理组织不会在企业 ESB 或 EAI 上公开服务的情况下,很难以点到点的方式集成两种不同的技术服务。
例如,在电信 IT 环境中:
- 将一个 SMS 发送到特定圆的 SMSC,公开为 SOAP web 服务或 EJB API;或
- 在 CRM 应用程序中创建服务请求,使用 MQ 或 JMS 绑定通过 ESB 公开为一个数据库存储的过程 (如 Oracle CRM);或者
- 创建 Sales Order(销售订单)请求,用于使用 SMSGateway 的移动 SMS 的 Distributor。
如果以上服务是由一个非 Java 应用程序使用的,那么使用 SOAP web 服务的集成会很麻烦,并需要更多的开发工作。
这种新方法可以用一种框架的形式实现,使它可以在 Java 服务公开为一种类 REST 的资源的其他领域中重新使用。这种方法类似于 Struts 框架方法,由以下组件组成(如下图所示):
图 1. 架构概述
![架构概述](http://www.ibm.com/developerworks/cn/webservices/ws-RESTservices/Figure_1_ArchitectureOverview.gif)
该架构包括 Front Controller,作为接收请求并向客户端提供响应的中心点。Front Controller 将请求处理委托给包含此框架处理逻辑的 ActionController。ActionController 执行验证,将请求映射到相应的 Action,并调用生成响应的动作。为请求处理、日志记录和异常处理这些可以被单个 Action 以及 ActionController 使用的动作提供了各种 Helper Service。
框架组件
下图展示了框架的各个组件以及彼此之间如何相互关联。
图 2. 框架组件
![框架组件](http://www.ibm.com/developerworks/cn/webservices/ws-RESTservices/Figure_2_FrameworkComponents.gif)
各种组件描述如下:
服务客户端
这是一个需要调用服务的客户端应用程序。此组件可以是基于 Java 的,也可以是任何其他客户端,只要它能够支持 HTTP 方法
通用组件
这些都是日志记录、 异常处理和任何常见功能所需的实用程序服务或者实现所需的常量。在示例代码中使用 Apache Commons 日志记录和 Log4j 实现。
RESTServiceServlet
框架使用 Front Controller 模式进行集中请求处理,并使用此 Java Servlet 组件处理输入的请求。它支持常见的 HTTP 方法,如 GET、PUT、POST 和 DELETE。
RESTActionController
此组件是核心框架控制器,管理加载服务和框架配置的核心功能,验证请求,映射请求与配置 REST 动作并执行动作。
RESTConfiguration
该组件负责在运行时加载和缓存框架配置,以及各种 REST 服务配置。此组件供 RESTActionController 用于确定请求要调用的正确动作,并验证输入请求。
RESTMapping
此组件存储在配置文件中指定的 REST 动作映射。映射主要包括客户端调用的 URI 和进行处理的动作类。
ActionContext
此组件封装执行 REST 动作所需的所有功能。它可以帮助开发人员提供请求和响应处理功能,因此开发人员只需要编写实际的业务逻辑实现代码。它向动作组件隐藏协议特定的请求和响应对象,因此允许独立测试 POJO 之类。它还提供一个指向 XML Binding Service 的句柄,让 Java 业务对象可以基于配置的 XML Binding API 方便地转换为 XML,反之亦然。RESTActionController 动态配置此组件,并将其提供给 Action 组件。
XMLBinding
此组件封装 Java XML Binding 机制,提供了一个统一的界面用于将 Java 业务对象转换为 XML,反之亦然。通过实现标准的框架接口,它支持任何 XML 绑定机制,如 JAXB、SDO、Castor 等。默认情况下,它为 XMLEncoder 和 XMLDecoder 提供了开箱即用功能。
Configuration XML
此组件包含框架和服务配置。当开发 REST 服务时,它们可以加入服务配置文件中。框架配置包含日志记录和 XML Binding 服务,不需要定期更换。
处理典型 POST 请求的这些组件之间的交互如下所示:
图 3. 组件交互
![组件交互](http://www.ibm.com/developerworks/cn/webservices/ws-RESTservices/Figure_3_ComponentInteraction.gif)
如上图所示,REST 服务配置最初加载,并缓存到 RESTConfiguration 组件中。对于 REST 服务的每个 HTTP 请求,RESTServiceServlet 组件将请求委托到 RESTActionController,它又会检索相应的映射、验证请求、创建 ActionContext 组件以及路径和查询输入,并调用 Action 类 (例如,createUserAction)。Action 类调用后端 Java 业务服务进行处理。
让所有组件运作起来
本节介绍了这篇文章提供的框架的示例实现。它展示了类关系图、各种配置文件和显示如何将上述设计付诸实行的代码片段。
注意: 示例代码可能没有遵循所有 Java 编码的最佳做法,因为它只是一个示例。
示例实现
下图显示了示例实现中的类。蓝色所示的类是框架外部的类,将它们放在这里是为了展示与框架的结构关系。
图 4. 类图
![类图](http://www.ibm.com/developerworks/cn/webservices/ws-RESTservices/Figure_4_ClassDiagram.gif)
配置文件
配置文件 "rest-services-config.xml"
包含 REST 服务表示形式和相应的 Java Action 之间的映射,如下:
清单 1. REST 服务配置
<?xml version="1.0" ?> <rest-config> <rest-api id="CreateUserProfile" uri="/Registration/CreateUser" method="POST"> <handler id="RegAction" class="ws.registration.restactions.CreateProfile"/> </rest-api> <rest-api id="GetUserProfile" uri="/Registration/GetUser" method="GET"> <handler id="RegAction" class=" ws.registration.restactions.GetProfile"/> </rest-api> ... </rest-config>
在该示例实现中,XML Binding 服务实现在 "rl-config.xml"
文件中配置的框架配置文件如下所示。通过修改此文件实现的任何自定义实现都可以接入,只要实现了 XMLBindingService 接口。
清单 2:框架配置
# XML Binding Implementation Service # Default implementation ws.rest.xmlbinding.service.impl=ws.rest.xmlbinding.service.impl.XMLEncDecServiceImpl
日志配置文件 "ws_log.properties"
指定 log4j 属性和日志文件的位置。这可以按需要作出适当修改。
Controller Servlet
RESTServiceServlet 在 web.xml 中配置,处理所有具有上下文路径的请求,其中上下文路径的 <web-app>/restservices/* 如下所示:
清单 3:Servlet 配置
<servlet> <description></description> <display-name>RESTServletService</display-name> <servlet-name>RESTServletService</servlet-name> <servlet-class>ws.rest.servlet.RESTServiceServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>RESTServletService</servlet-name> <url-pattern>/restservices/*</url-pattern> </servlet-mapping>
REST Action
对于每个 REST 资源,例如 GetUserProfile,都将创建一个实现 ActionInterface 的相应动作类。该接口定义了动作类需要实现的 "doExecute(ActionContext ctx)"
方法。ActionContext 提供服务,获取 REST 路径输入或查询参数,获取 XMLBindingService 实例并将 XML 输出发送到客户端,不公开 Action 的协议细节。PathInputs 是一个包含路径值的 List 对象,路径值的顺序与它们在 URL 中指定的顺序相同。
清单 4:Action 代码片段
public class GetProfile implements ActionInterface { public void doExecute(ActionContext context) throws Exception { // Get the value from URL path String userName = context.getPathInputs().get(0); // Invoke backend service to retrieve user profile UserProfileBean bean = getUser(userName); // Serialize the bean using framework service and send response String xml = context.getXMLBindingService().serialize(bean); // Use the ActionContext to generate XML and context.sendResponse(response, xml); }
动作类负责使用超类中的 XMLBindingService 以 XML 形式生成输出。请查看示例实现的 ws.registration.restactions.GetProfile
类。ActionContext 还可以提供协议特定的 HttpServletRequest 和 HttpServletResponse 对象,以防需要自定义处理。它还提供了 Path 值和 URL 参数。
XML Binding
该代码示例提供了一个 Java XML Binding 的实现,该实现使用 java.beans.XMLEncoder
和 java.beans.XMLDecoder
类。XML Binding 服务实现接受一个 JavaBean 对象,并将其转换为上述 Encoder 和 Decoder 相应的 XML 表示形式。如果需要 JAXB 实现,那么可以开发一个实现 ws.rest.xmlbinding.service.XMLBindingService
接口的实现类。
执行示例服务
示例代码分发包含示例 WAR 文件 "RESTWS.war"
,它可以部署在 Tomcat 容器中(已在 Apache Tomcat 版本 6.0.20 上进行了测试)。JDK 要求是 JDK 1.5 以上。
成功部署该应用程序之后,在浏览器中输入以下 URL:http://localhost:8080/RESTWS/html/CreateProfile.html ,如下所示:
图 5. 创建 Profile Service 输入
![创建 Profile Service 输入](http://www.ibm.com/developerworks/cn/webservices/ws-RESTservices/Figure_5_CreateProfileServiceInput.jpg)
该页面调用 REST 服务
POST <url-prefix>/Registration/CreateProfile
您可以修改在 <string></string> 标记中指定的 XML 值。
注意:请注意 XML 结构依赖于 JavaBean 对象和 Java 使用的 XML 序列化技术。
提交时,动作类显示成功消息,表示后端服务的调用。可以查看 ws_log.log
文件调试消息。
图 6. 创建 Profile Service 输出
![创建 Profile Service 输出](http://www.ibm.com/developerworks/cn/webservices/ws-RESTservices/Figure_6_CreateProfileServiceOutput.jpg)
类似地,实现示例 GET <url-prefix>/Registration/GetProfile/{username}
服务以检索配置文件,如下图所示:
图 7. 执行 Profile Service
![执行 Profile Service](http://www.ibm.com/developerworks/cn/webservices/ws-RESTservices/Figure_7_GetProfileServiceExecution.jpg)
小结
因此,使用这一框架,我们认为我们可以轻松地将任何 Java 服务公开为一种 REST API。通过使用 XML 作为输出数据,服务可以方便地集成以任何非 Java 语言编写的客户端应用程序,只要它支持 HTTP 协议。此外,实现 Action 类大大减少了开发工作。
可以开展进一步工作,使用适当的异常处理和缓存服务加强实现。也可以实现注释支持,协助开发人员创建默认配置,无需在配置文件中指定它。
致谢
我真诚地感谢我的同事 Rajesh Mishra 先生,他帮助我审查内容,提供了宝贵的建议和评论。这不仅帮助我提高了框架的可用性,而且还使其外观更加完美。