Spring 3 创建 RESTful Web Services

在 Java™ 中,您可以使用以下几种方法来创建 RESTful Web Service:使用 JSR 311(311)及其参考实现 Jersey、使用 Restlet 框架和从头开始开发。Spring 是流行的 Java EE 应用开发框架,现在它的 MVC 层也支持 REST 了。本文将介绍使用 Spring 开发 RESTful Web Services 的方法。读者将了解如何使用 Spring API 和注释来开发 RESTful Web Services,以及 Spring 是如何将这个新特性整合到它的框架中的。

引言

Roy Fielding 是 HTTP 1.0 和 1.1 标准的主要作者之一,2000 年,他在他的博士论文中首次提出了 REST。

通过 REST 风格体系架构,请求和响应都是基于资源表示的传输来构建的。资源是通过全局 ID 来标识的,这些 ID 一般使用的是一个统一资源标识符(URI)。客户端应用使用 HTTP 方法(如,GET、POST、PUT 或 DELETE)来操作一个或多个资源。通常,GET 是用于获取或列出一个或多个资源,POST 用于创建,PUT 用于更新或替换,而 DELETE 则用于删除资源。

例如,GET http://host/context/employees/12345 将获取 ID 为 12345 的员工的表示。这个响应表示可以是包含详细的员工信息的 XML 或 ATOM,或者是具有更好 UI 的 JSP/HTML 页面。您看到哪种表示方式取决于服务器端实现和您的客户端请求的 MIME 类型。

RESTful Web Service 是一个使用 HTTP 和 REST 原理实现的 Web Service。通常,一个 RESTful Web Service 将定义基本资源 URI、它所支持的表示/响应 MIME,以及它所支持的操作。

本文将介绍如何使用 Spring 创建 Java 实现的服务器端 RESTful Web Services。这个例子将使用浏览器、curl 和 Firefox 插件 RESTClient 作为发出请求的客户端。您可以 下载 本文所使用的源代码。

本文假定您是熟悉 REST 基本知识的。中有更多关于 REST 的信息。

 

Spring 3 的 REST 支持

在 Spring 框架支持 REST 之前,人们会使用其他几种实现技术来创建 Java 的 RESTful Web Services,如 Restlet、RestEasy 和 Jersey。Jersey 是其中最值得注意的,它是 JAX-RS(JSR 311)的参考实现中有更多关于 JSR 311 和 Jersey 的信息。

Spring 是一个得到广泛应用的 Java EE 框架,它在版本 3 以后就增加了 RESTful Web Services 开发的支持。虽然,对 REST 的支持并不是 JAX-RS 的一种实现,但是它具有比标准定义更多的特性。REST 支持被无缝整合到 Spring 的 MVC 层,它可以很容易应用到使用 Spring 构建的应用中。

Spring REST 支持的主要特性包括:

  • 注释,如 @RequestMapping 和 @PathVariable,支持资源标识和 URL 映射
  • ContentNegotiatingViewResolver 支持为不同的 MIME/内容类型使用不同的表示方式
  • 使用相似的编程模型无缝地整合到原始的 MVC 层
 

创建一个示例 RESTful Web Service

本节中的例子将演示 Spring 3 环境的创建过程,并创建一个可以部署到 Tomcat 中的 “Hello World” 应用。然后我们再完成一个更复杂的应用来了解 Spring 3 REST 支持的重要概念,如多种 MIME 类型表示支持和 JAXB 支持。另外,本文还使用一些代码片断来帮助理解这些概念。您可以 下载 本文的所有示例代码。

本文假定您已经熟悉 Spring 框架和 Spring MVC。

Hello World:使用 Spring 3 REST 支持

要创建这个例子所使用的开发环境,您需要:

  • IDE:Eclipse IDE for JEE (v3.4+)
  • Java SE5 以上
  • Web 容器:Apache Tomcat 6.0(Jetty 或其他容器也可)
  • Spring 3 框架(v3.0.3 是本文编写时的最新版本)
  • 其他程序库:JAXB 2、JSTL、commons-logging

在 Eclipse 中创建一个 Web 应用,然后设置 Tomcat 6 作为它的运行环境。然后,您需要设置 web.xml 文件来激活 Spring WebApplicationContext。这个例子将 Spring bean 配置分成两个文件:rest-servlet.xml 包含与 MVC/REST 有关的配置,rest-context.xml 包含服务级别的配置(如数据源 beans)。清单 1 显示了 web.xml 中的 Spring 配置的部分。

清单 1. 在 web.xml 中激活 Spring WebApplicationContext
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/rest-context.xml
</param-value>
</context-param> <!-- This listener will load other application context file in addition to
rest-servlet.xml -->
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener> <servlet>
<servlet-name>rest</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet> <servlet-mapping>
<servlet-name>rest</servlet-name>
<url-pattern>/service/*</url-pattern>
</servlet-mapping>

在 rest-servlet.xml 文件中创建 Spring MVC 的相关配置(Controller、View、View Resolver)。清单 2 显示了其中最重要的部分。

清单 2. 在 rest-servlet.xml 文件中创建 Spring MVC 配置
<context:component-scan base-package="dw.spring3.rest.controller" />

<!--To enable @RequestMapping process on type level and method level-->
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" /> <!--Use JAXB OXM marshaller to marshall/unmarshall following class-->
<bean id="jaxbMarshaller"
class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>dw.spring3.rest.bean.Employee</value>
<value>dw.spring3.rest.bean.EmployeeList</value>
</list>
</property>
</bean> <bean id="employees" class=
"org.springframework.web.servlet.view.xml.MarshallingView">
<constructor-arg ref="jaxbMarshaller" />
</bean> <bean id="viewResolver" class=
"org.springframework.web.servlet.view.BeanNameViewResolver" />

上面的代码中:

Component-scan
启用对带有 Spring 注释的类进行自动扫描
在实践中,它将检查控制器类中所定义的 @Controller 注释。
DefaultAnnotationHanlderMappings 和 AnnotationMethodHandlerAdapter
使用 @ReqeustMapping 注释的类或函数的 beans 由 Spring 处理
这个注释将在下一节进行详细介绍。
Jaxb2Mashaller
定义使用 JAXB 2 进行对象 XML 映射(OXM)的编组器(marshaller)和解组器(unmarshaller
MashallingView
定义一个使用 Jaxb2Mashaller 的 XML 表示 view
BeanNameViewResolver
使用用户指定的 bean 名称定义一个视图解析器 
本例将使用名为 “employees” 的 MarshallingView

这样就完成了 Spring 的相关配置。下一步是编写一个控制器来处理用户请求。清单 3 显示的是控制器类。

清单 3. dw.spring3.rest.controller.EmployeeController
@Controller
publicclass EmployeeController {
@RequestMapping(method=RequestMethod.GET, value="/employee/{id}")
public ModelAndView getEmployee(@PathVariable String id) {
Employee e = employeeDS.get(Long.parseLong(id));
returnnew ModelAndView(XML_VIEW_NAME, "object", e);
}
}

@RequestMapping 注释是 Spring REST 特性的关键所在。它指定所注释的方法将处理哪个 HTTP 方法(RequestMethod.GET)和哪个 URI(/employee/{id})。注意:

  • 对于 {id} 占位符,使用 @PathVariable 注释可以将 {} 内的值注入到函数的参数。
  • XML_VIEW_NAME 为 employees,这是 rest-servlet.xml 中定义的视图名称。
  • employeeDS 是一个基于内存的数据源,它的实现已经超出本文写作范围。

将 Web 应用发布到您的 Tomcat 上。这时,您可以打开浏览器,然后输入 http://<host>:<port>/<appcontext>/service/employee/1。浏览器就会显示一个 ID 为 1 的员工信息的 XML 视图。

请继续阅读以了解更多关于 Spring REST 支持的特性。

方法

资源操作是通过 HTTP 方法实现的,如 GET、POST、PUT 和 DELETE。您在前面已经了解了如何使用 GET 方法查询员工信息。现在我们将介绍 POST、PUT 和 DELETE。

通过使用 @RequestMapping 注释的功能,处理不同方法的代码是非常相似的。清单 4 显示了 EmployeeController 的代码片断。

清单 4. dw.spring3.rest.controller.EmployeeController
@RequestMapping(method=RequestMethod.POST, value="/employee")
public ModelAndView addEmployee(@RequestBody String body) {
Source source = new StreamSource(new StringReader(body));
Employee e = (Employee) jaxb2Mashaller.unmarshal(source);
employeeDS.add(e);
returnnew ModelAndView(XML_VIEW_NAME, "object", e);
} @RequestMapping(method=RequestMethod.PUT, value="/employee/{id}")
public ModelAndView updateEmployee(@RequestBody String body) {
Source source = new StreamSource(new StringReader(body));
Employee e = (Employee) jaxb2Mashaller.unmarshal(source);
employeeDS.update(e);
returnnew ModelAndView(XML_VIEW_NAME, "object", e);
} @RequestMapping(method=RequestMethod.DELETE, value="/employee/{id}")
public ModelAndView removeEmployee(@PathVariable String id) {
employeeDS.remove(Long.parseLong(id));
List<Employee> employees = employeeDS.getAll();
EmployeeList list = new EmployeeList(employees);
returnnew ModelAndView(XML_VIEW_NAME, "employees", list);
}

在上面的代码中:

  • RequestMethod.<Method> 的值确定所注释的函数应该处理哪个 HTTP 方法。
  • 通过 @RequestBody,HTTP 请求的主体内容可以作为一个参数注入。

    在本例中,主体内容是表示员工信息的 XML 数据。我们使用 JAXB 2 来将 XML 解组为 Java Bean,然后将它存储。一个示例请求主体可以是:

    <employee><id>3</id><name>guest</name></employee>
  • 其他可以注入到函数参数的有用的注释有 @PathVariable@RequestParm 等等。Spring 文档有完整列表的注释。

资源集合

通常,您还需要操作批量的资源。例如,您可能希望获取所有员工的信息而不只是一个员工的信息。您可以采取类似于之前情况的方法实现;您所需要做的修改就是将 URI 从 /employee 修改成 /employees。员工的复数形式能够正确反映批量的语义。清单 5 显示了这种实现方法。

清单 5. EmployeeController 的 getAllEmployees
@RequestMapping(method=RequestMethod.GET, value="/employees")
public ModelAndView getEmployees() {
List<Employee> employees = employeeDS.getAll();
EmployeeList list = new EmployeeList(employees);
returnnew ModelAndView(XML_VIEW_NAME, "employees", list);
}

您需要为 Employee 集合声明一个包装类。这个包装类是 JAXB 2 所需要的,因为它无法正确地编组 java.util.List 类。清单 6 显示了EmployeeList 类。

清单 6. dw.spring3.rest.bean.EmployeeList
@XmlRootElement(name="employees")
publicclass EmployeeList {
privateint count;
private List<Employee> employees; public EmployeeList() {} public EmployeeList(List<Employee> employees) {
this.employees = employees;
this.count = employees.size();
} publicint getCount() {
return count;
}
publicvoid setCount(int count) {
this.count = count;
} @XmlElement(name="employee")
public List<Employee> getEmployees() {
return employees;
}
publicvoid setEmployees(List<Employee> employees) {
this.employees = employees;
} }

内容协商

REST 服务的另一个常用特性是它们能够根据请求产生不同的表示。例如,如果客户端请求所有员工的 HTML/text 表示方式,那么服务器就应该为用户产生一个符合语法规则的 HTML 页面。如果客户端请求的是员工的 application/XML 表示方式,那么服务器就应该产生一个 XML 结果。其他受欢迎的表示方式还有 ATOM 和 PDF。

Spring 3 引入了一个名为 ContentNegotiatingViewResolver 的新视图解析器。它可以根据请求的内容类型(请求头中的 Accept 属性)或 URI 后缀来切换视图解析器。下面的例子使用 ContentNegotiatingViewResolver 来实现多种表示方式的支持。

在 rest-servlet.xml 文件中,用注释去掉原来定义的 viewResolver。而使用 ContentNegotiatingViewResolver 来替代它,如 清单 7 所示。

清单 7. 定义内容协商
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="mediaTypes">
<map>
<entry key="xml" value="application/xml"/>
<entry key="html" value="text/html"/>
</map>
</property>
<property name="viewResolvers">
<list>
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
<bean class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value=
"org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</list>
</property>
</bean>

这个定义显示了处理两种请求内容的支持:application/xml 和 text/html。这段代码也定义了两个视图解析器:其中BeanNameViewResolver 是负责处理 application/xml 的,而另一个 UrlBasedViewResolver 则负责处理 text/html。

在实践中,当您在浏览器上输入 http://<host>:<port>/<appcontext>/service/employees,那么它就会请求 text/html 形式的所有员工信息。然后 UrlBasedViewResolver 会进行解析,而 Spring 将会选择 /WEB-INF/jsp/employees.jsp 作为返回的视图。当您添加请求头Accept:application/xml 并再发起请求时,那么 BeanNameViewResolver 就会进行解析。如 清单 5 中的代码,它将使用一个名为 employees的视图来表示,它就是所定义的 JAXB 2 编组器视图。

getAllEmployees() 的控制器代码不需要修改。employees.jsp 将会使用名为 employees 的模型对象来渲染。清单 8 显示了 employees.jsp 的代码片断。

清单 8. /WEB-INF/jsp 中的 employees.jsp
<table border=1>
<thead><tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
</tr></thead>
<c:forEach var="employee" items="${employees.employees}">
<tr>
<td>${employee.id}</td>
<td>${employee.name}</td>
<td>${employee.email}</td>
</tr>
</c:forEach>
</table>
 

与 REST 服务通信的客户端

现目前为止,您已经开发一个简单的支持对员工信息的 CRUD(增删查改)操作的 RESTful Web Service。接下来这一节将介绍如何与这个服务进行通信。您将使用 curl 来测试这个 REST 服务。

您也可以使用名为 RESTClient 的 Firefox 插件来测试 REST 服务。它很容易使用且带有良好的 UI。 有关于它的下载信息。

使用 curl

Curl 是一个流行的能以 HTTP 和 HTTPS 协议向服务器发送请求的命令行工具。Curl 是 Linux® 和 Mac® 上的一个内置工具。对于 Windows® 平台,您可以另外下载这个工具。

要初始化查询所有员工信息的第一个 curl 命令,您可以输入:

curl –HAccept:application/xml
http://localhost:8080/rest/service/employees

它的响应将是 XML 格式的,并且包含所有的员工信息,如图 1 所示。

图 1. XML 方式表示的所有员工信息

您也可以在浏览器上尝试访问相同的 URL。这时,头信息中的 Accept 为 text/html,所以浏览器会显示 employees.jsp 中定义的一个表格。图 2 显示的是这种情况。

图 2. HTML 方式表示的所有员工信息

要将一个新的员工信息 POST 到服务器上,我们可以使用下面的代码。清单 4 中的 addEmployee() 代码将会使用请求体并将它解组为Employee 对象。

curl -X POST -HContent-type:application/xml --data
"<employee><id>3</id><name>guest</name><email>guest@ibm.com</employee>"
http://localhost:8080/rest/service/employee

这样就添加了一个新的员工信息。您可以使用第一个例子来验证员工列表。

PUT 方法类似于 POST。

curl -X PUT -HContent-type:application/xml --data
"<employee><id>3</id><name>guest3</name><email>guest3@ibm.com</employee>"
http://localhost:8080/rest/service/employee/3

上面的代码修改了 ID 为 3 的员工数据。

结束语

现在 Spring 3 已经在它的 MVC 层上支持 REST 了,您可以使用 Spring API 和注释来开发 RESTful Web Services 了。本文的示例向您介绍了如何使用一些能够帮您简化开发 Java 服务器端 RESTful Web Services 的 Spring 3 的新特性。

Spring 3 来创建 RESTful Web Services的更多相关文章

  1. 使用 Spring 3 来创建 RESTful Web Services(转)

    使用 Spring 3 来创建 RESTful Web Services 在 Java™ 中,您可以使用以下几种方法来创建 RESTful Web Service:使用 JSR 311(311)及其参 ...

  2. 使用 Spring 3 来创建 RESTful Web Services

    来源于:https://www.ibm.com/developerworks/cn/web/wa-spring3webserv/ 在 Java™ 中,您可以使用以下几种方法来创建 RESTful We ...

  3. 基于Spring设计并实现RESTful Web Services(转)

    基于Spring设计并实现RESTful Web Services 在本教程中,你将会使用Spring来创建一个具有生产力的RESTful网络服务. 为什么用RESTful网络服务? 从和Amazon ...

  4. CRUD using Spring MVC 4.0 RESTful Web Services and AngularJS

    国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html内部邀请码:C8E245J (不写邀请码,没有现金送)国内私 ...

  5. 使用Java创建RESTful Web Service

    REST是REpresentational State Transfer的缩写(一般中文翻译为表述性状态转移).2000年Roy Fielding博士在他的博士论文“Architectural Sty ...

  6. 使用Java创建RESTful Web Service(转)

    REST是REpresentational State Transfer的缩写(一般中文翻译为表述性状态转移).2000年Roy Fielding博士在他的博士论文“Architectural Sty ...

  7. spring3创建RESTFul Web Service

    spring 3支持创建RESTFul Web Service,使用起来非常简单.不外乎一个@ResponseBody的问题. 例如:后台controller: 做一个JSP页面,使用ajax获取数据 ...

  8. Spring Boot - Building RESTful Web Services

    Spring Boot Building RESTful Web Services https://www.tutorialspoint.com/spring_boot/spring_boot_bui ...

  9. Spring Boot 构建一个 RESTful Web Service

    1  项目目标: 构建一个 web service,接收get 请求 http://localhost:8080/greeting 响应一个json 结果: {"id":1,&qu ...

随机推荐

  1. HTML-点击收藏功能模块

    先上效果图: 功能简单: 附上源码以及注解 <div class="info-attribute" id="collect"> <input ...

  2. http方法

    http method(方法):1.get 从服务器获取资源2.post 向服务器发送资源3.put 向服务器推送资源4.delete 告诉服务器删除某个资源5.head 告诉服务器返回数据时不需要返 ...

  3. Linq101-Conversion Operators

    using System; using System.Linq; namespace Linq101 { class Conversion { /// <summary> /// This ...

  4. (转)ThinkPHP3.0 使用分组后路径访问无效的解决方法!

    注意,清除Runtime,就是清除缓存,很重要,妹的,调试了一下午,总是加上Home目录分组就找不到页面,直接放到action下就行,原来是缓存搞得鬼,另外要在入口文件开启‘APP_DEBUG’ 在T ...

  5. (转)php 函数名称前的@有什么作用

    如:$register_globals = @ini_get('register_globals'); 隐藏错误提示~如果ini_get('register_globals'); 语句错误的话`错误会 ...

  6. Python os常用模块

    Python的标准库中的os模块包含普遍的操作系统功能.如果你希望你的程序能够与平台无关的话,这个模块是尤为重要的.即它允许一个程序在编写后不需要任何改动,也不会发生任何问题,就可以在Linux和Wi ...

  7. CSS 布局Float 【1】

    1.HTML元素分类 HTML元素大题可分为内联(inline)元素和块(block)元素. 1.1 内联元素(inline) ①元素显示方式:"文本方式",1个挨着1个,不独自占 ...

  8. 七、C# 接口

    并非只能通过继承使用多态性,还能通过接口使用它. 和抽象类不同,接口不包含任何实现(方法). 然后和抽象类相似,接口也定义了一系列成员,调用者可以依赖这些成员来支持一个特定的功能.   实现接口的类会 ...

  9. 【NEERC 2003】有向图破坏

    [题目描述] Alice和Bob正在玩如下的游戏.首先Alice画一个有N个顶点,M条边的有向图.然后Bob试着摧毁它.在一次操作中他可以找到图中的一个点,并且删除它所有的入边或所有的出边. Alic ...

  10. 装饰者模式(Decorator)

    首先来看一个例子: 比如,饮料可以分为很多种类,而这里我取一个咖啡,那么这个咖啡呢,有多种形式的, 比如有加糖了的咖啡,有加奶的咖啡,也有加热了的咖啡,也有加了冰块的咖啡. 而各个顾客的选择却是不同的 ...