使用Spring来创建RESTful Web Services


本文的代码可从该代码下载链接进行下载


引言

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

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

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

本文将介绍如何使用Spring创建Java实现的服务器端RESTful Web Services。

Spring的REST支持

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

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支持。另外,本文还使用一些代码片段来帮助理解这些概念。

Hello World:使用Spring 3 REST支持

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

  • IDE:MyEclipse IDE
  • Java SE5以上
  • Web容器:Apache Tomcat 7.0
  • Spring 3框架
  • 所需要的JAR包截图如下:

JAR文件


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

清单1.在web.xml中激活Spring WebApplicationContext

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>SpringRest</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  
  <!-- The context params that read by ContextLoaderListener -->
  <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 springweb-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>
  
  <jsp-config>
  	<taglib>
  		<taglib-uri>http://java.sun.com/jsp/jstl/core</taglib-uri>
  		<taglib-location>/WEB-INF/tags/c.tld</taglib-location>
  	</taglib>
  </jsp-config>
</web-app>

在rest-servlet.xml文件中创建Spring MVC的相关配置(Controller、View、View Resolver)。清单2显示了rest-servlet.xml的配置信息。

清单2.在rest-servlet.xml文件中创建Spring MVC配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
		 xmlns:p="http://www.springframework.org/schema/p"
	     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	     xmlns:context="http://www.springframework.org/schema/context"
	     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">


	<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" />

	<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 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 id="viewResolver" 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>
	
	<!--bean id="viewResolver" class="org.springframework.web.servlet.view.BeanNameViewResolver" /-->

	<bean id="employeeController" class="dw.spring3.rest.controller.EmployeeController">
		<property name="employeeDS" ref="employeeDS" />
		<property name="jaxb2Mashaller" ref="jaxbMarshaller" />
	</bean>

</beans>

上面的代码中:

component-scan

启用对带有Spring注释的类进行自动扫描

在实践中,它将检查控制器类中所定义的@Controller注释。

DefaultAnnotationHandlerMappings和AnnotationMethodHandlerAdapter

使用@RequestMapping注释的类或函数的beans由Spring处理。

Jaxb2Marshaller

定义使用JAXB 2进行对象XML映射(OXM)的编组器(marshaller)和解组器(unmarshaller)。

MarshallingView

定义一个使用Jaxb2Marshaller的XML表示view

BeanNameViewResolver

使用用户指定的bean名称定义一个视图解析器

本例将使用名为"employees"的MarshallingView。


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

清单3.dw.spring3.rest.controller.EmployeeController

@Controller
public class EmployeeController {
	@RequestMapping(method=RequestMethod.GET, value="/employee/{id}")
	public ModelAndView getEmployee(@PathVariable String id) {
		Employee e = employeeDS.get(Long.parseLong(id));
		return new 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视图。

employee 1

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

方法

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

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

清单4.dw.spring3.rest.controller.EmployeeController

	@RequestMapping(method=RequestMethod.PUT, value="/employee/{id}")
	public ModelAndView updateEmployee(@RequestBody String body) {
		Source source = new StreamSource(new StringReader(body));
		Employee e = (Employee) jaxb2Marshaller.unmarshal(source);
		employeeDS.update(e);
		return new ModelAndView(XML_VIEW_NAME, "object", e);
	}
	
	@RequestMapping(method=RequestMethod.POST, value="/employee")
	public ModelAndView addEmployee(@RequestBody String body) {
		Source source = new StreamSource(new StringReader(body));
		Employee e = (Employee) jaxb2Marshaller.unmarshal(source);
		employeeDS.add(e);
		return new 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);
		return new ModelAndView(XML_VIEW_NAME, "employees", list);
	}

在上面的代码中:

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

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

    <employee>
        <id>3</id>
        <name>Black</name>
    </employee>

  • 其他可以注入到函数参数的有用的注释有@PathVariable、@RequestParam等等,具体可查看Spring文档。

资源集合

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

清单5.EmployeeController的getAllEmplyees

	@RequestMapping(method=RequestMethod.GET, value="/employees")
	public ModelAndView getEmployees() {
		List<Employee> employees = employeeDS.getAll();
		EmployeeList list = new EmployeeList(employees);
		return new ModelAndView(XML_VIEW_NAME, "employees", list);
	}

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

清单6.dw.spring3.rest.bean.EmployeeList

package dw.spring3.rest.bean;

import java.util.List;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name="employees")
public class EmployeeList {

	private int count;
	
	private List<Employee> employees;

	public EmployeeList() {	}

	public EmployeeList(List<Employee> employees) {
		this.employees = employees;
		this.count = employees.size();
	}

	public int getCount() {
		return count;
	}

	public void setCount(int count) {
		this.count = count;
	}

	@XmlElement(name="employee")
	public List<Employee> getEmployees() {
		return employees;
	}

	public void setEmployees(List<Employee> employees) {
		this.employees = employees;
	}

}

内容协商

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

Spring 3引入了一个名为ContentNegotiatingViewResolver的新视图解析器。它可以根据请求的内容类型(请求头中的Accept属性)或URI后缀来切换视图解析器。下面的例子使用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 id="viewResolver" 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 Services。接下来我们将介绍如何与这个服务进行通信,将使用curl来测试这个REST服务。

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

使用curl

curl是一个流行的能以HTTP和HTTPS协议向服务器发送请求的命令行工具。curl是Linux和Mac上的一个内置的工具。

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

employees

查看响应文件employees.xml,它是XML格式的,并且包含所有的员工信息,如下图所示:

xml结果

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

html结果


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

curl -X POST -HContent-type:application/xml --data "<employee><id>3</id><name>Black</name><email>xablackwang@163.com</email></employee>" http://localhost:12345/SpringRest/service/employee

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

PUT方法类似于POST。

curl -X PUT -HContent-type:application/xml --data "<employee><id>3</id><name>White</name><email>xawhitema@163.com</email></employee>" http://localhost:12345/SpringRest/service/employee/3

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



总结

Spring和REST进行结合,可以方便您利用Sring API和注释开发出RESTful Web Services。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值