使用 REST 式 Web 服务实现 Oracle 和 Microsoft 技术交互作者:John Charles (Juan Carlos) Olamendy Turruellas 本指南指导我们如何使用 Jersey 框架和 Oracle JDeveloper 11g 开发 REST 式 Web 服务。 2009 年 12 月发布 REST 式 Web 服务是 Web 应用程序开发和分布式编程(将运行于不同平台上的大量企业应用程序集成在一起)方面的最新创新。表示状态传输 (REST) 是一套体系结构原则,用于定义并寻址 Web 资源而无需使用重量级的 SOAP 协议栈(WS-* 栈)。从 REST 的角度看,每个 Web 应用程序是一个服务,因此可以使用基本的 Web 技术(如 HTTP、URI 命名标准、XML 和 JSON 解析器)轻松开发 Web 服务。(REST 式 Web 服务的历史始于 Roy Fielding 的博士学位论文 — 体系结构风格与基于网络的软件体系结构的设计 的第 5 章,作为 HTTP 规范的作者之一,Fielding 提出 REST 时不是将其作为一个参考体系结构,而是作为一种用于规范分布式体系结构的方法。) REST 式 Web 服务的关键之处在于,在服务器端将应用程序的状态和功能抽象为资源。这些资源可通过 URI 命名根据全局标识符来唯一地引用,并且共享一个与客户端通讯的统一接口,该接口包含一组已定义好的操作和内容类型。传统的 HTTP 方法 — POST、GET、PUT 和 DELETE(在 REST 术语中又称为动词)— 涵盖了可对数据执行的各种操作,包括创建、读取、更新和删除 (CRUD) 操作。GET 方法用于执行读取操作,该操作返回资源的内容。POST 方法用于在服务器上创建资源并为该资源分配一个引用。PUT 方法用于在客户端向服务器提交内容时更新资源。最后一个方法 DELETE 用于从服务器中删除资源。 URI 命名的原则是必须易于理解、结构良好,这样客户端可通过资源 URI 直接访问应用程序的任意状态,而无需经过中间层。建议使用路径变量以层次结构方式分隔路径的各个元素。例如,要获取客户列表,URI 可以为 http://server/customers,而要获取标识符为 1234 的客户,URI 可以为 http://server/customers/1234。必须使用标点字符来分隔层次结构中同级的多个数据。例如,要获取标识符为 1234 和 5678 的客户,URI 可以为 http://server/customers/1234;5678,用分号分隔两个标识符。最后一个技巧是,使用查询变量来命名参数 — URI 命名用于指定资源而不是操作,因此将操作名称放入 URI 中是不合适的。例如,如果要删除客户 1234,应避免使用 URI http://server/deletecustomers/1234。解决方法是重载 HTTP 方法(POST、PUT 和 DELETE)。 资源间交换的数据可以表示为 MIME 类型,如 XML 或 JSON 文档以及图像、纯文本或其他内容格式。这是通过请求中的 Content-Type 头指定的。应用程序中使用的格式取决于您的需求。如果要传输结构化数据,可以使用 XML、YAML、JSON 或 CSV 格式。如果要传输文档,可以使用 HTML、DocBook、SGML、ODF、PDF 或 PostScript 之类的格式。您还可以处理不同的内容来处理图片(JPG、PNG、BMP)、日历信息 (iCal) 以及分类链接 (OPML)。 最后,REST 式 Web 服务需要使用协议来传输状态,这些状态必须是客户端/服务器的、无状态的、可缓存的和分层的,这样可以有任意数量的连接器(客户端、服务器、缓存、通道、防火墙、网关、路由器)透明地转发请求。 REST 式 Web 服务有两种类型的状态:资源和应用程序。资源状态是有关资源的信息,保留在服务器端,只能以表示的形式发送给客户端。应用程序状态是有关客户端通过应用程序采用的路径信息,一直保留在客户端,直到可以用来创建、修改和删除资源。 REST 式 Web 服务本质上是无状态的,如果客户端希望在请求中包含状态,则必须将该信息作为底层请求的一部分提交。无状态性这一特性对于支持解决方案中的可伸缩性非常重要,因为服务器端不保存任何信息(由客户端负责保存)并且以前的请求中不隐含任何状态信息。如果您有一个负载平衡器,当一台服务器无法处理某个请求时,可由另一服务器处理该请求,因为消息请求是自包含的,我们不需要重构解决方案体系结构。 Ruby on Rails 是一个针对 Ruby 编程语言的开源 Web 开发框架。您可以使用 REST 原则非常轻松地创建一个提供关系数据的 Web 应用程序。另外一个开源 Web 开发框架 Django 是针对 Python 编程语言编写的,它遵循模型-视图-控制器 (MVC) 设计模式。 最后,JAX-RS (JSR 311) 为 Java 开发人员提供了一个 API,用于根据 REST 原则创建 REST 式 Web 服务。JAX-RS 使用批注(在 Java 5 及更高版本中)以简化 Web 服务构件的开发工作,这样您可以将简单的传统 Java 对象 (POJO) 作为 Web 资源来提供。有几种基于 JAX-RS 的实现,如 Jersey、JBoss RESTEasy、Restlet、Apache CXF 和 Triaxrs。本文介绍使用 Jersey 框架(Sun 的 JAX-RS 参考实现)和 Oracle JDeveloper 11g 开发 REST Web 服务。(截至本文撰写时,Oracle 正计划在不久的将来通过使用 Jersey 框架方法结合 Oracle JDeveloper 工具和 Oracle WebLogic Server 来支持 JAX-RS。) 要搭建使用 Jersey 框架开发 REST 式 Web 服务的环境,请从 https://jersey.dev.java.net/ 下载 Jersey 库及所有需要的相关项。登录此网站后,您会看到 Jersey 包含几个主要部件:
使用 Oracle 技术开发 REST 式 Web 服务本文的示例应用程序反映了下面这种常见的业务场景:客户端需要使用 Web 服务搜索有关业务实体的详细信息,而客户端应用程序和服务器应用程序运行在不同的平台上。REST 式 Web 服务是用 Oracle JDeveloper 11g 和运行在 Oracle 平台上的 Jersey 框架开发的。前端应用程序是一个控制台程序,是用 Microsoft Visual Studio .NET 2008 和 Microsoft .NET Framework 3.5 开发的,它使用 Web 服务并显示底层客户端数据。我们将使用 REST 式 Web 服务技术作为客户端和服务器之间的关键集成层。 我们从服务器端应用程序开始。打开 Oracle JDeveloper 11g IDE,通过在对话框中输入应用程序名称和保存文件的目录来创建一个新应用程序。 在 JAX-RS 标准中,资源类是一个 POJO 类,用 @Path 进行批注以表示特定的 REST 资源,其中至少有一个方法用 @Path 进行批注,或者用请求方法指示符(如 @GET、@PUT、@POST 或 @DELETE)进行批注来处理请求和特定操作(如创建、读取、更新和删除资源)。 第一步是定义业务实体,其状态将保存在 REST 式消息的有效载荷中。在此例中,我们要发送有关客户的信息,因此我们将在 domainobjects 程序包中创建一个 Customer 类(如下所示)。 下一步是指定所有 REST 请求均应重定向到 Jersey 容器。这包括在应用程序的 web.xml 文件中定义一个 servlet 调度程序。在本例中,ServletRestfulWS servlet 将映射到 /resources/* 模式,用于访问 REST 资源的基本 URL 为 http://{remote_server}:{remote_port}/{app_context}/resources/。我们还包括 index.jsp 页面作为应用程序入口点的欢迎页面,尽管这不是必需的。除了声明该 Jersey servlet 外,我们还需要定义一个初始化参数以指示包含资源的 Java 程序包。在本例中,我们用 Java 开发的资源位于 restfulresources 程序包中(如下所示)。 现在,我们将对本例中使用的批注进行解释。首先,所有批注都在 JAX-RS (JSR 311) 规范的 javax.ws.rs.* 部分中定义。由于在 REST 世界中,资源是关键元素,因此我们需要一种访问资源的方法。@Path 批注标识资源响应的 URI 路径模板。URI 路径模板相对于服务器和端口(此例中为 localhost 和 7101)的基本 URI、Web 应用程序的环境根目录(此例中为 RestfulWebserviceApp-CustomerLookupRestfulWebService-context-root)以及 Jersey servlet 响应的 URI 模式(此例中为 web.xml 中定义的资源)。REST 中有关 URI 路径模板的这一方法对于为的 REST 应用程序动态构建 URI 很有帮助。在我们的应用程序中,根资源用 @Path("customers") 进行批注,这样它就成了对我们应用程序的客户实体进行操作的入口点。由于资源可以有子资源(一种访问底层资源类的属性和方法的途径),我们可以使用 @Path("list") 批注定义一个用于访问 getCustomerList 方法的子资源,然后使用 http://{remote_server}:{remote_port}/{app_context}/resources/customers/list URI 访问该方法。 我们还可以使用 @Path("customer/{identifier}") 批注定义一个用于访问 getCustomer 方法的子资源,然后使用 http://{remote_server}:{remote_port}/{app_context}/resources/customers/customer/1 URI 访问标识符为 1 的客户。在本例中,URI 路径模板包括变量,这些变量用花括号指示并在运行时由 Jersey 框架进行替换。为了获取这些变量的值并将其与底层请求方法相匹配,我们在定义方法参数时需要使用 @PathParam("identifier") 批注。 两个方法中的 @GET 批注是一个请求方法指示符(@POST、@PUT、@DELETE 和 @HEAD 批注同样如此),在 JAX-RS 中定义,并且对应于具有相似名称的 HTTP 方法。这意味着这两个方法只处理 HTTP 的 GET 请求。 @Produces 批注指明响应支持的 MIME 类型。本例中为 application/xml,尽管可以是任意内容格式(如图像、JSON、HTML 或纯文本)。 为了在嵌入式 Oracle WebLogic Server 中成功地部署和运行 REST 式应用程序,我们需要使用一个技巧,因为 Oracle JDeveloper 11g 不知道如何将 Jersey 应用程序部署到 Oracle WebLogic Server 中。我们需要在该 Web 应用程序的 WEB-INF 目录中随 web.xml 文件创建一个 weblogic.xml 文件(如下所示)。 使用 Internet Explorer 浏览 REST 式应用程序时,您将看到如下所示的结果。 通过 Microsoft .NET 技术使用 REST 式 Web 服务为了创建该解决方案的客户端部分,我们打开 Visual Studio .NET 2008,选择 File -> New -> Project,然后在 Project Types 树中转到 Visual C# 子树中的 Windows 节点。选中 Console Application from Templates,然后为该项目、解决方案以及将用于保存底层文件的目录分别输入描述性的名称(如下所示)。 在本文的示例应用程序中,我将通过第二种方法来使用 REST 式 Web 服务。System.Net.HttpWebRequest 实现了处理对 Web 服务器的请求和响应的逻辑。为了创建 Web 请求,我们需要使用工厂方法(用传递的 URI 作为参数)创建 HttpWebRequest 请求类的实例。在本例中,该 URI 实例是根据传递给应用程序的参数动态生成的。如果没有参数且我们想要的是客户列表,我们将使用 http://127.0.0.1:7101/RestfulWebserviceApp-CustomerLookupRestfulWebService-context-root/resources/customers/list 地址。否则,该地址将根据代表客户标识符的参数而定,例如,对于标识符为 1 的客户,我们使用 http://127.0.0.1:7101/RestfulWebserviceApp-CustomerLookupRestfulWebService-context-root/resources/customers/customer/1。 之后,当我们通过返回 System.Net.HttpWebResponse 类的一个实例调用 GetResponse 方法时,系统将发送该请求。由于 HttpWebResponse 类实现 System.IDisposable 接口以释放外部资源,在 using 块中调用 GetResponse 方法以便在不再需要 WebResponse 实例时可以调用 Dispose 方法,从而可以关闭网络连接。 如果对服务器的 Web 请求导致 HTTP 错误代码(4xx 或 5xx),将抛出一个 WebException 实例。否则,会调用 HttpWebResponse 实例的 GetResponseStream 方法,结果是返回一个 System.IO.Stream 实例,可读取该实例以处理响应的有效载荷。 在本例中,消息的有效载荷包含一个表示客户列表的 XML 文档。要直接处理该 XML 文档,我们需要将流(表示 XML 有效载荷)加载到一个 XPathDocument 中。 可以用两种方法来解析 XML 文档。第一种方法是将 XML 文档反序列化到一个表示业务实体的对象模型。这是更好的解决方法,因为这种方法清晰地将持久的中介逻辑和业务逻辑分隔开。另一种方法是直接解析 XML 文档并以解析过程中的形式显示结果。这种方法的不便之处在于会出现语义不匹配,因为我们不了解数据及其结构的含义。在本示例中,我们只是显示响应消息的有效载荷而不考虑任何语义问题,所以我们将采用第二种方法,这样就不会偏离我们的主题 — REST 式解决方案(参见下面的代码段)。 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Net; using System.Xml.XPath; namespace RestfulWSClientConsoleApp { class Program { static void Main(string[] args) { Uri uriRestfulWS = null; if (args.Length>0) { string strUri = String.Format("http://127.0.0.1:7101/RestfulWebserviceApp-CustomerLookupRestfulWebService-context-root/resources/customers/customer/{0}", args[0]); uriRestfulWS = new Uri(strUri); } else { uriRestfulWS = new Uri("http://127.0.0.1:7101/RestfulWebserviceApp-CustomerLookupRestfulWebService-context-root/resources/customers/list"); } try { HttpWebRequest objWebRequest = (HttpWebRequest)WebRequest.Create(uriRestfulWS); using (HttpWebResponse objWebResponse = (HttpWebResponse)objWebRequest.GetResponse()) { XPathDocument objXmlDoc = new XPathDocument(objWebResponse.GetResponseStream()); XPathNavigator objXPathNav = objXmlDoc.CreateNavigator(); foreach (XPathNavigator objNode in objXPathNav.Select("/Customers/Customer")) { string strCustomerId = objNode.SelectSingleNode("CustomerId").ToString(); string strFullname = objNode.SelectSingleNode("Fullname").ToString(); string strAge = objNode.SelectSingleNode("Age").ToString(); System.Console.WriteLine("Customer Information. Id={0}, Fullname={1}, Age={2}", strCustomerId, strFullname, strAge); } } } catch (WebException objEx) { System.Console.WriteLine("Web Exception calling the RESTful Web service. Message={0}", objEx.Message); } } } }最后,我们运行一下这个控制台应用程序,将客户标识符 1 作为参数传递进去。其输出如下所示。 总结本文介绍如何利用 Oracle 技术(如 Oracle JDeveloper 11g)、Jersey 框架(JAX-RS [JSR 311] 规范的参考实现)以及 Oracle WebLogic Server 来创建 REST 式 Web 服务,以及如何通过 Microsoft 技术(如 Visual Studio .NET 2008 和 .NET 3.5 框架)来使用 Web 服务。既然您已阅读了本文,现在就可以使用这个创新型的方法来扩展您自己的 Web 解决方案了。 Juan Carlos (John Charles) Olamendy Turruellas [ johnx_olam@fastmail.fm] 是一位高级集成解决方案架构师、开发人员和顾问。他的主要工作是面向对象的分析和设计、数据库设计和重构、使用设计模式的企业应用程序体系结构和集成,以及软件开发流程管理。他在以下方面具有广泛的经验:使用 Microsoft 和 Oracle 平台开发企业应用程序、分布式系统编程、业务流程集成、遵循面向服务体系结构 (SOA) 的原则的消息传递及相关技术。他曾多次获得 Microsoft 最具价值专家 (MVP) 称号,是一位 Oracle ACE。 |