Develop a RESTful Web Service with Spring

Develop a RESTful Web Service with Spring


在 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 作为发出请求的客户端。您可以 下载 本文所使用的源代码。


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

详见:http://www.ibm.com/developerworks/cn/web/wa-spring3webserv


摘录部分

方法

资源操作是通过 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 等等。


资源集合

通常,您还需要操作批量的资源。例如,您可能希望获取所有员工的信息而不只是一个员工的信息。您可以采取类似于之前情况的方法实现;您所需要做的修改就是将 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。


总结

以前一直在使用spring mvc,知道看到了这篇文章,对spring mvc才有了更深入的了解。之前竟然不知道spring mvc可以做restful。现在想想以前做的那些spring mvc的项目,是否可以叫做restful?我想还有一些差距,应该是基本上实现了restful,但整体开发思想和思路和方法没有严格按照rest的要求来做,只是形式上接近了restful的风格。。

======END======


你可能感兴趣的:(Develop a RESTful Web Service with Spring)