使用 Spring 3 来创建 RESTful Web Services

引言

RoyFielding是HTTP1.0和1.1标准的主要作者之一,2000年,他在他的博士论文中首次提出了REST。

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

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

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

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

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

--------------------------------------------------------------------------------

回页首

Spring3的REST支持

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

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

SpringREST支持的主要特性包括:

注释,如@RequestMapping和@PathVariable,支持资源标识和URL映射

ContentNegotiatingViewResolver支持为不同的MIME/内容类型使用不同的表示方式

使用相似的编程模型无缝地整合到原始的MVC层

--------------------------------------------------------------------------------

回页首

创建一个示例RESTfulWebService

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

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

HelloWorld:使用Spring3REST支持

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

IDE:EclipseIDEforJEE(v3.4+)

JavaSE5以上

Web容器:ApacheTomcat6.0(Jetty或其他容器也可)

Spring3框架(v3.0.3是本文编写时的最新版本)

其他程序库:JAXB2、JSTL、commons-logging

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

清单1.在web.xml中激活SpringWebApplicationContext

<context-param>

<param-name>contextConfigLocation</param-name>

<param-value>

/WEB-INF/rest-context.xml

</param-value>

</context-param>

<!--Thislistenerwillloadotherapplicationcontextfileinadditionto

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文件中创建SpringMVC的相关配置(Controller、View、ViewResolver)。清单2显示了其中最重要的部分。

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

<context:component-scanbase-package="dw.spring3.rest.controller"/>

<!--Toenable@RequestMappingprocessontypelevelandmethodlevel-->

<beanclass="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>

<beanclass="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>

<!--UseJAXBOXMmarshallertomarshall/unmarshallfollowingclass-->

<beanid="jaxbMarshaller"

class="org.springframework.oxm.jaxb.Jaxb2Marshaller">

<propertyname="classesToBeBound">

<list>

<value>dw.spring3.rest.bean.Employee</value>

<value>dw.spring3.rest.bean.EmployeeList</value>

</list>

</property>

</bean>

<beanid="employees"class=

"org.springframework.web.servlet.view.xml.MarshallingView">

<constructor-argref="jaxbMarshaller"/>

</bean>

<beanid="viewResolver"class=

"org.springframework.web.servlet.view.BeanNameViewResolver"/>

上面的代码中:

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

在实践中,它将检查控制器类中所定义的@Controller注释。DefaultAnnotationHanlderMappings和AnnotationMethodHandlerAdapter使用@ReqeustMapping注释的类或函数的beans由Spring处理

这个注释将在下一节进行详细介绍。Jaxb2Mashaller定义使用JAXB2进行对象XML映射(OXM)的编组器(marshaller)和解组器(unmarshallerMashallingView定义一个使用Jaxb2Mashaller的XML表示viewBeanNameViewResolver使用用户指定的bean名称定义一个视图解析器

本例将使用名为“employees”的MarshallingView。

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

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

@Controller

publicclassEmployeeController{

@RequestMapping(method=RequestMethod.GET,value="/employee/{id}")

publicModelAndViewgetEmployee(@PathVariableStringid){

Employeee=employeeDS.get(Long.parseLong(id));

returnnewModelAndView(XML_VIEW_NAME,"object",e);

}

}

@RequestMapping注释是SpringREST特性的关键所在。它指定所注释的方法将处理哪个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视图。

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

方法

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

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

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

@RequestMapping(method=RequestMethod.POST,value="/employee")

publicModelAndViewaddEmployee(@RequestBodyStringbody){

Sourcesource=newStreamSource(newStringReader(body));

Employeee=(Employee)jaxb2Mashaller.unmarshal(source);

employeeDS.add(e);

returnnewModelAndView(XML_VIEW_NAME,"object",e);

}

@RequestMapping(method=RequestMethod.PUT,value="/employee/{id}")

publicModelAndViewupdateEmployee(@RequestBodyStringbody){

Sourcesource=newStreamSource(newStringReader(body));

Employeee=(Employee)jaxb2Mashaller.unmarshal(source);

employeeDS.update(e);

returnnewModelAndView(XML_VIEW_NAME,"object",e);

}

@RequestMapping(method=RequestMethod.DELETE,value="/employee/{id}")

publicModelAndViewremoveEmployee(@PathVariableStringid){

employeeDS.remove(Long.parseLong(id));

List<Employee>employees=employeeDS.getAll();

EmployeeListlist=newEmployeeList(employees);

returnnewModelAndView(XML_VIEW_NAME,"employees",list);

}

在上面的代码中:

RequestMethod.<Method>的值确定所注释的函数应该处理哪个HTTP方法。

通过@RequestBody,HTTP请求的主体内容可以作为一个参数注入。

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

<employee><id>3</id><name>guest</name></employee>

其他可以注入到函数参数的有用的注释有@PathVariable、@RequestParm等等。Spring文档有完整列表的注释(见参考资料)。

资源集合

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

清单5.EmployeeController的getAllEmployees

@RequestMapping(method=RequestMethod.GET,value="/employees")

publicModelAndViewgetEmployees(){

List<Employee>employees=employeeDS.getAll();

EmployeeListlist=newEmployeeList(employees);

returnnewModelAndView(XML_VIEW_NAME,"employees",list);

}

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

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

@XmlRootElement(name="employees")

publicclassEmployeeList{

privateintcount;

privateList<Employee>employees;

publicEmployeeList(){}

publicEmployeeList(List<Employee>employees){

this.employees=employees;

this.count=employees.size();

}

publicintgetCount(){

returncount;

}

publicvoidsetCount(intcount){

this.count=count;

}

@XmlElement(name="employee")

publicList<Employee>getEmployees(){

returnemployees;

}

publicvoidsetEmployees(List<Employee>employees){

this.employees=employees;

}

}

内容协商

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

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

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

清单7.定义内容协商

<beanclass="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">

<propertyname="mediaTypes">

<map>

<entrykey="xml"value="application/xml"/>

<entrykey="html"value="text/html"/>

</map>

</property>

<propertyname="viewResolvers">

<list>

<beanclass="org.springframework.web.servlet.view.BeanNameViewResolver"/>

<beanclass="org.springframework.web.servlet.view.UrlBasedViewResolver">

<propertyname="viewClass"value=

"org.springframework.web.servlet.view.JstlView"/>

<propertyname="prefix"value="/WEB-INF/jsp/"/>

<propertyname="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的视图来表示,它就是所定义的JAXB2编组器视图。

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

清单8./WEB-INF/jsp中的employees.jsp

<tableborder=1>

<thead><tr>

<th>ID</th>

<th>Name</th>

<th>Email</th>

</tr></thead>

<c:forEachvar="employee"items="${employees.employees}">

<tr>

<td>${employee.id}</td>

<td>${employee.name}</td>

<td>${employee.email}</td>

</tr>

</c:forEach>

</table>

--------------------------------------------------------------------------------

回页首

与REST服务通信的客户端

现目前为止,您已经开发一个简单的支持对员工信息的CRUD(增删查改)操作的RESTfulWebService。接下来这一节将介绍如何与这个服务进行通信。您将使用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-XPOST-HContent-type:application/xml--data

"<employee><id>3</id><name>guest</name><email>[email protected]</employee>"

http://localhost:8080/rest/service/employee

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

PUT方法类似于POST。

curl-XPUT-HContent-type:application/xml--data

"<employee><id>3</id><name>guest3</name><email>[email protected]</employee>"

http://localhost:8080/rest/service/employee/3

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

--------------------------------------------------------------------------------

回页首

结束语

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

相关推荐