相关文章推荐
霸气的小蝌蚪  ·  extractlbpfeatures - ...·  1 年前    · 
英俊的哑铃  ·  Mysql ...·  2 年前    · 
安静的熊猫  ·  Http failure response ...·  2 年前    · 
捣蛋的凉茶  ·  排查Microsoft ...·  2 年前    · 
Oracle Account

Manage your account and access personalized content. Sign up for an Oracle Account

Sign in to Cloud

Access your cloud dashboard, manage orders, and more. Sign up for a free trial

开发人员:SOA

使用 REST 式 Web 服务实现 Oracle 和 Microsoft 技术交互

作者:John Charles (Juan Carlos) Olamendy Turruellas ACE

本指南指导我们如何使用 Jersey 框架和 Oracle JDeveloper 11 g 开发 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 11 g 开发 REST Web 服务。(截至本文撰写时,Oracle 正计划在不久的将来通过使用 Jersey 框架方法结合 Oracle JDeveloper 工具和 Oracle WebLogic Server 来支持 JAX-RS。)

要搭建使用 Jersey 框架开发 REST 式 Web 服务的环境,请从 https://jersey.dev.java.net/ 下载 Jersey 库及所有需要的相关项。登录此网站后,您会看到 Jersey 包含几个主要部件:

  • 核心服务器。 一组用于开发 REST 式 Web 服务的批注和 API(在 JSR-311 中得以标准化)
  • 核心客户端。 用来与 REST 服务通讯的客户端 API
  • 集成。 一组用于将 Jersey 与 Spring、Guice、Apache Abdera 等集成的库
  • 对于我们的 REST 演示解决方案,我们只需要 Jersey 下载文件中的以下三个 Java 存档 (JAR) 文件:jersey.jar、asm-3.1.jar 和 jsr311-api.jar。

    使用 Oracle 技术开发 REST 式 Web 服务

    本文的示例应用程序反映了下面这种常见的业务场景:客户端需要使用 Web 服务搜索有关业务实体的详细信息,而客户端应用程序和服务器应用程序运行在不同的平台上。REST 式 Web 服务是用 Oracle JDeveloper 11 g 和运行在 Oracle 平台上的 Jersey 框架开发的。前端应用程序是一个控制台程序,是用 Microsoft Visual Studio .NET 2008 和 Microsoft .NET Framework 3.5 开发的,它使用 Web 服务并显示底层客户端数据。我们将使用 REST 式 Web 服务技术作为客户端和服务器之间的关键集成层。

    我们从服务器端应用程序开始。打开 Oracle JDeveloper 11 g IDE,通过在对话框中输入应用程序名称和保存文件的目录来创建一个新应用程序。 图 1:创建新应用程序

    下一步是通过选择 File -> New 为该应用程序添加一个 Web 项目。出现 New Gallery 对话框后,在 Categories 窗格中选择 Projects 节点,然后选择 Web Project 项。 图 2:创建 Web 项目

    将显示 Create Web Project 向导。单击 Next 。在 Location 页面中,输入项目名称以及在该应用程序的工作目录中的项目路径。然后单击 Next 图 3:Create Web Project 向导的 Location 页面

    在 Web Application 页面中,选择 Servlet 2.5\JSP 2.1 (Java EE 1.5) 设置应用程序要使用的 Web 技术。单击 Next 。在 Page Flow Technology 页面中,选择 None ,然后单击 Next 。在 Tag Libraries 页面中,不选择任何标记库,单击 Next 转到下一页面。该向导的最后一页是 Web Project Profile 页面,在该页面中输入文档根路径、Web 应用程序名称以及上下文名称。单击 Finish 图 4:Create Web Project 向导的 Web Project Profile 页面

    现在,我们需要将 Jersey 框架包括到 Oracle JDeveloper 11 g 的库中,方法是:选择 Tool -> Manage Libraries 启动 Manage Libraries 对话框,然后单击 New 打开 Create Library 对话框。下一步是为这个受管理的库输入一个易于理解的名称,如 Jersey Framework,然后单击 Add Entry 浏览到要部署 Jersey 库的目录。在本示例中,我们需要包括 asm-3.1.jar、jersey.jar 和 jsr-311.jar JAR。记住要选中 Deployed by Default 复选框。 图 5:添加 Jersey 框架的 JAR 文件

    单击 OK 后,返回到 Manage Library 对话框。设置如下所示。 图 6:Jersey 框架添加到 Oracle JDeveloper 11 g 管理的库中。

    接下来,右键单击该 Web 项目并选择 Properties 。转到 Libraries and Classpath 节点,单击 Add Library ,然后选择 Jersey Framework 库。Libraries and Classpath 的设置如下所示。 图 7:Libraries and Classpath 设置

    现在,根据 REST 式 Web 服务的编程模型,我们需要定义一个应用程序,将它的状态和功能抽象为资源。每个资源均可通过 URI 进行唯一寻址。所有资源共享一个统一的接口,从而可以通过使用基本的 HTTP 操作以及使用任意已有的内容格式(字符串、XML、JSON 等)发送对象数据来从资源和客户端传输状态和功能。每个资源调用业务服务来处理请求,然后创建底层响应。

    在 JAX-RS 标准中,资源类是一个 POJO 类,用 @Path 进行批注以表示特定的 REST 资源,其中至少有一个方法用 @Path 进行批注,或者用请求方法指示符(如 @GET、@PUT、@POST 或 @DELETE)进行批注来处理请求和特定操作(如创建、读取、更新和删除资源)。

    第一步是定义业务实体,其状态将保存在 REST 式消息的有效载荷中。在此例中,我们要发送有关客户的信息,因此我们将在 domainobjects 程序包中创建一个 Customer 类(如下所示)。

    图 8:在 domainobjects 程序包中创建 Customer 类

    接下来,我们定义客户实体类型的特性及 setter 和 getter 属性,如标识符、全名及年龄这些字段。在实际情况中,我们必须使用许多字段来描述一个客户实体。我们还必须重写 toString 方法以便返回客户的 XML 表示。我们对 Customer 类的定义如下所示。 图 9:在 domainobjects 程序包中创建 Customer 类

    我们还需要定义一个客户实体管理器。这个实体管理器负责主要的数据操作,代表客户实体充当到数据库的网关。为了将注意力集中到 REST 式 Web 服务的开发而不是数据访问对象的创建上,本文避免任何与数据库系统的交互,因此定义一个 Java 客户数组来模拟底层查询操作。然后,该实体管理器将使用 getCustomers 方法返回可用客户列表,并在指定客户标识符的情况下使用 getCustomerById 方法获取特定客户信息。 图 10:CustomerEntityManager 类的定义

    在需要与关系数据源交互的实际情况下,建议使用 Oracle JDeveloper 11 g 内置的特性和 Oracle 应用开发框架 (Oracle ADF) 技术(数据控制和数据绑定)开发一个健壮的持久层。

    下一步是指定所有 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 程序包中(如下所示)。

    图 11:在 web.xml 文件中配置 Jersey 调度程序

    下一步是定义一个名为 CustomerResource 的资源,用于接受 HTTP GET 请求并返回有关客户的信息。我们将在 restfulresources 程序包中创建 CustomerResource 类(如下所示)。 图 12:在 restfulresources 程序包中创建 CustomerResource 类

    CustomerResource 类是应用程序的根资源类。根资源类是 POJO 类,它们或者用 @Path 进行批注,或者至少有一个方法用 @Path 或请求方法指示符(如 @GET、@PUT、@POST 或 @DELETE)进行批注。在本例中,我们将获取有关客户实体的信息。CustomerResource 类的代码如下所示。 图 13:CustomerResource 类的定义

    CustomerResource 类的业务逻辑通过 REST Web 服务公开,用于获取客户列表(调用 getCustomerList 方法)和特定客户的详细信息(通过调用 getCustomer 方法并将客户标识符作为参数传递给该方法)。

    现在,我们将对本例中使用的批注进行解释。首先,所有批注都在 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 11 g 不知道如何将 Jersey 应用程序部署到 Oracle WebLogic Server 中。我们需要在该 Web 应用程序的 WEB-INF 目录中随 web.xml 文件创建一个 weblogic.xml 文件(如下所示)。

    图 14:Oracle WebLogic Server 部署文件

    最后,我们再使用一个技巧,将一个空的 index.jsp 页面添加到项目中。之后,右键单击此 JSP 页面,从上下文菜单中选择 Run 选项。当应用程序服务器启动并且应用程序部署到其上后,您就可以查看结果了。

    使用 Internet Explorer 浏览 REST 式应用程序时,您将看到如下所示的结果。

    图 15:REST 式 Web 服务返回的客户列表

    下面的屏幕截图显示标识符为 1 的客户的详细信息。 图 16:REST 式 Web 服务返回的标识符为 1 的客户信息

    通过 Microsoft .NET 技术使用 REST 式 Web 服务

    为了创建该解决方案的客户端部分,我们打开 Visual Studio .NET 2008,选择 File -> New -> Project ,然后在 Project Types 树中转到 Visual C# 子树中的 Windows 节点。选中 Console Application from Templates ,然后为该项目、解决方案以及将用于保存底层文件的目录分别输入描述性的名称(如下所示)。

    图 17:创建新的控制台应用程序项目

    在 Microsoft .NET 3.5 和 Windows Communication Foundation (WCF) 3.5 中,可通过两个方法来使用 REST 式 Web 服务。第一个方法是使用新的 WebHttpBinding,WebHttpBinding 用于对通过 HTTP 请求(而不是 SOAP 消息)公开的端点进行配置。这样,我们可通过使用 URI,发送 HTTP 请求,将响应反序列化到对象模型,即可轻松调用一个服务。WCF 支持不同的消息格式,如 XML、JSON 和原始二进制数据。另一种使用 REST 式服务的方法是,手动创建一个 HTTP 请求(将所有参数作为 URI 的一部分包括进来),获取响应,然后解析响应的有效载荷数据。

    在本文的 示例应用程序 中,我将通过第二种方法来使用 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); uriRestfulWS = new Uri("http://127.0.0.1:7101/RestfulWebserviceApp-CustomerLookupRestfulWebService-context-root/resources/customers/list"); 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 作为参数传递进去。其输出如下所示。 图 18:执行使用 REST 式 Web 服务的客户端程序

    本文介绍如何利用 Oracle 技术(如 Oracle JDeveloper 11 g )、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。