使用Spring 3 MVC HttpMessageConverter功能构建RESTful Web服务

配套文章“使用Spring 3构建RESTful Web服务”介绍了构建RESTful Web服务的“ Spring方式”。 它还说明了如何使用ContentNegotiatingViewResolver生成多个表示形式,这是RESTful Web服务的重要功能。 本文介绍了使用HttpMessageConverter生成多种表示形式的另一种方法,并且本文中的示例演示了如何将RestTemplateHttpMessageConverter一起使用以与服务进行通信。

Spring MVC中的REST支持

本部分概述了支持RESTful Web服务的主要Spring功能或注释。

@Controller
使用@Controller批注来注释将成为MVC中的控制器并处理HTTP请求的类。
@RequestMapping
使用@RequestMapping批注来注释应处理某些HTTP方法,URI或HTTP标头的函数。 该注释是Spring REST支持的关键。 您更改method参数以处理其他HTTP方法。

例如:

@RequestMapping(method=RequestMethod.GET, value="/emps", 
headers="Accept=application/xml, application/json")
@PathVariable
可以使用@PathVariable批注将URI中的路径变量作为参数注入。

例如:

@RequestMapping(method=RequestMethod.GET, value="/emp/{id}")
public ModelAndView getEmployee(@PathVariable String id) { … }
其他有用的注释
使用@RequestParam将URL参数注入该方法。

使用@RequestHeader将特定的HTTP标头注入该方法。

使用@RequestBody将HTTP请求主体注入该方法。

使用@ResponseBody返回内容或对象作为HTTP响应主体。

如果您将HttpEntity<T>作为参数提供,则可以使用它来自动注入该方法。

使用ResponseEntity<T>返回带有自定义状态或标头的HTTP响应。

例如:

public @ResponseBody Employee getEmployeeBy(@RequestParam("name") 
String name, @RequestHeader("Accept") String accept, @RequestBody String body) {…} 
public ResponseEntity<String> method(HttpEntity<String> entity) {…}

请参阅Spring文档以获取可以注入到方法中的受支持的注释或对象的完整列表。

多代表支持

用不同的MIME类型表示相同的资源是RESTful Web服务的重要方面。 通常,您将使用具有不同“接受” HTTP标头的相同URI来获取具有不同表示形式的资源。 您还可以使用其他URI或具有不同请求参数的URI。

“使用Spring 3构建RESTful Web服务”引入了ContentNegotiatingViewResolver ,它可以选择一个不同的视图解析器来处理相同的URI(具有不同的accept标头)。 因此, ContentNegotiatingViewResolver可用于生成多个表示形式。

还有另一种产生多个表示形式的方法-通过组合HttpMessageConverterc@ResponseBody批注。 使用这种方法,您无需使用View技术。

HttpMessageConverter

HTTP请求和响应是基于文本的,这意味着浏览器和服务器通过交换原始文本进行通信。 但是,对于Spring,控制器类中的方法返回纯的“ String”类型和域模型(或其他Java内置对象)。 Spring如何将对象序列化/反序列化为原始文本? 这由HttpMessageConverter处理。 Spring捆绑了可以满足您共同需求的实现。 表1显示了一些示例。

表1. HttpMessageConverter示例
用... 您可以...
StringHttpMessageConverter 从请求和响应中读取/写入字符串。 默认情况下,它支持媒体类型text / *,并使用text / plain的Content-Type进行写入。
FormHttpMessageConverter 从请求和响应中读取/写入表单数据。 默认情况下,它读取媒体类型application / x-www-form-urlencoded并将数据写入MultiValueMap <String,String>。
封送处理HttpMessageConverter 使用Spring的marshaller / un-marshaller读写XML数据。 它转换媒体类型为application / xml的数据。
MappingJacksonHttpMessageConverter 使用Jackson的ObjectMapper读/写JSON数据。 它转换媒体类型为application / json的数据。
AtomFeedHttpMessageConverter 使用ROME的Feed API读/写ATOM提要。 它将转换媒体类型为application / atom + xml的数据。
RssChannelHttpMessageConverter 使用ROME的feed API读/写RSS feed。 它将转换媒体类型为application / rss + xml的数据。

构建RESTful Web服务

在本节中,学习构建简单的RESTful Web服务,该服务可以产生多种表示形式。 示例应用程序中使用的一些资源是在“使用Spring 3构建RESTful Web服务”中构建的。 您也可以下载示例代码。

首先,您必须配置HttpMessageConverter 。 要产生多种表示形式,请自定义几个HttpMessageConverter实例以将对象转换为不同的媒体类型。 本节介绍JSON,ATOM和XML媒体类型。

JSON格式

让我们从最简单的示例开始。 JSON是一种轻量级的数据交换格式,人类易于读写。 清单1显示了配置JSON转换器的代码。

清单1.在rest-servlet.xml中配置HttpMessageConverter
<bean class="org.springframework.web.servlet.mvc.annotation
.AnnotationMethodHandlerAdapter">
   <property name="messageConverters">
       <list>
           <ref bean="jsonConverter" />
   <ref bean="marshallingConverter" />
   <ref bean="atomConverter" />
       </list>
   </property>
</bean>

<bean id="jsonConverter" 
            class="org.springframework.http.converter.json
.MappingJacksonHttpMessageConverter">
   <property name="supportedMediaTypes" value="application/json" />
</bean>

在该配置中,注册了三个转换器。 MappingJacksonHttpMessageConverter用于将对象转换为JSON,反之亦然。 该内置转换器使用Jackson的ObjectMapper将JSON映射到JavaBean,因此您必须将以下Jackson Jackson JAR文件添加到类路径。

  • org.codehaus.jackson.jar
  • org.codehaus.jackson.mapper.jar

下一步是编写一种方法来处理请求JSON表示的请求。 清单2显示了详细信息。

清单2.处理在EmployeeController中定义的JSON请求
@RequestMapping(method=RequestMethod.GET, value="/emp/{id}", 
		headers="Accept=application/json")
public @ResponseBody Employee getEmp(@PathVariable String id) {
Employee e = employeeDS.get(Long.parseLong(id));
return e;
}
	
@RequestMapping(method=RequestMethod.GET, value="/emps", 
		headers="Accept=application/json")
public @ResponseBody EmployeeListinggetAllEmp() {
List<Employee> employees = employeeDS.getAll();
EmployeeListinglist = new EmployeeList(employees);
return list;
}

@ResponseBody批注用于使返回对象( EmployeeEmployeeList )成为响应主体内容,该内容将由MappingJacksonHttpMessageConverter映射为JSON。

通过使用HttpMessageConverter@ResponseBody ,您可以实现多种表示形式而无需使用Spring的View技术-与使用ContentNegotiatingViewResolver相比,这是一个优势。

现在,您可以使用CURL或REST Client Firefox插件来调用请求。 记住要添加一个HTTP标头: Accept=application/json 。 清单3以JSON格式显示了所需的响应。

清单3. getEmp()和getAllEmp()的JSON结果
Response for /rest/service/emp/1
{"id":1,"name":"Huang Yi Ming","email":"huangyim@cn.ibm.com"}

Response for /rest/service/emps
{"count":2,
"employees":[
{"id":1,"name":"Huang Yi Ming","email":"huangyim@cn.ibm.com"},
{"id":2,"name":"Wu Dong Fei","email":"wudongf@cn.ibm.com"}
]}

XML格式

Spring的内置转换器MarshallingHttpMessageConverter用于在对象和XML(OXM)之间进行映射。 该示例将JAXB 2用作转换器的编组器/取消编组器。 清单4显示了配置。

清单4.配置MarshallingHttpMessageConverter
<bean id="marshallingConverter" 
class="org.springframework.http.converter.xml
		.MarshallingHttpMessageConverter">
<constructor-arg ref="jaxbMarshaller" />
    <property name="supportedMediaTypes" value="application/xml"/>
      </bean>

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

重要的是要了解,JAXB 2对将java.util.List <T>映射到XML没有很好的支持。 常见的做法是为对象集合添加包装器类。 请参阅“使用Spring 3构建RESTful Web服务”或下载源代码以获取此JAXB带注释的类的详细信息。

控制器中处理请求的方法如何? 回顾一下清单2中的代码。 发现您无需在此处添加任何代码也就不足为奇了。 您只需要在Accept标头中添加另一种受支持的媒体类型,如下所示。

headers=”Accept=application/json, application/xml”

转换器会将对象正确映射到您请求的类型(JSON或XML)。 清单5显示了当您请求application / xml表示形式时所需的结果。

清单5. getEmp()和getAllEmp()的XML结果
Response for /rest/service/emp/1
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <employee>
   <email>huangyim@cn.ibm.com</email>
   <id>1</id>
   <name>Huang Yi Ming</name>
 </employee>
Response for /rest/service/emps
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  <employees>
  <count>2</count>
    <employee>
      <email>huangyim@cn.ibm.com</email>
      <id>1</id>
      <name>Huang Yi Ming</name>
    </employee>
    <employee>
      <email>wudongf@cn.ibm.com</email>
      <id>2</id><name>Wu Dong Fei</name>
    </employee>
 </employees>

ATOM提要

ATOM提要是用于在RESTful Web服务中交换数据的另一种流行格式。 Atom提要文档表示Atom提要,包括有关提要的元数据以及与之关联的一些或全部条目。 它的根是atom:feed元素。 还有一个ATOM发布协议(APP)来定义交换格式和行为。 (定义ATOM和APP格式不在本文讨论范围之内。

该示例使用AtomFeedHttpMessageConverter转换ATOM提要,该提要利用了ROME ATOM API。 因此,必须在类路径中包含JAR文件sun.syndication.jar。 清单6显示了此转换器的配置。

清单6.配置AtomFeedHttpMessageConverter
<bean id="atomConverter" 
class="org.springframework.http.converter.feed
		.AtomFeedHttpMessageConverter">
<property name="supportedMediaTypes" value="application/atom+xml" />
</bean>

清单7显示了处理ATOM请求和提要生成的代码。

清单7. EmployeeController和AtomUtil类中的getEmpFeed()
@RequestMapping(method=RequestMethod.GET, value="/emps", 
		headers="Accept=application/atom+xml")
public @ResponseBody Feed getEmpFeed() {
	List<Employee> employees = employeeDS.getAll();
	return AtomUtil.employeeFeed(employees, jaxb2Mashaller);
}

public static Feed employeeFeed(
	List<Employee> employees, Jaxb2Marshaller marshaller) {
Feed feed = new Feed();
feed.setFeedType("atom_1.0");
feed.setTitle("Employee Atom Feed");
		
List<Entry> entries = new ArrayList<Entry>();
for(Employee e : employees) {
	StreamResult result = new StreamResult(
	new ByteArrayOutputStream());
	marshaller.marshal(e, result);
	String xml = result.getOutputStream().toString();
			
	Entry entry = new Entry();
	entry.setId(Long.valueOf(e.getId()).toString());
	entry.setTitle(e.getName());
	Content content = new Content();
	content.setType(Content.XML);
	content.setValue(xml);
	
	List<Content> contents = new ArrayList<Content>();
	contents.add(content);
	entry.setContents(contents);
	entries.add(entry);
}
feed.setEntries(entries);
return feed;
}

在上面的代码中,请注意:

  • 所述getEmpFeed()方法处理相同的URI作为getAllEmp()但具有不同的Accept报头。
  • 使用employeeFeed()方法,可以将Employee对象编组为XML,然后将其添加到feed条目的<content>元素中。

清单8显示了当您请求URI / rest / service / emps的application / atom + xml表示形式时的输出。

清单8.请求application / atom + xml时/ rest / service / emps的输出
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Employee Atom Feed</title>

 <entry>
    <title>Huang Yi Ming</title>
    <id>1</id>
  <content type="xml">
    <employee>
            <email>huangyim@cn.ibm.com</email>
            <id>1</id>
            <name>Huang Yi Ming</name>
    </employee>
  </content>
</entry>

  <entry>
    <title>Wu Dong Fei</title>
    <id>2</id>
  <content type="xml">
    <employee>
            <email>wudongf@cn.ibm.com</email>
            <id>2</id>
            <name>Wu Dong Fei</name>
     </employee>
   </content>
 </entry>
 
</feed>

实现POST,PUT和DELETE

到目前为止的示例已经实现了几种方法来处理HTTP GET方法。 清单9显示了POSTPUTDELETE方法的实现。

清单9. EmployeeController中的POST,PUT和DELETE方法
@RequestMapping(method=RequestMethod.POST, value="/emp")
public @ResponseBody Employee addEmp(@RequestBody Employee e) {
employeeDS.add(e);
return e;
}
	
@RequestMapping(method=RequestMethod.PUT, value="/emp/{id}")
public @ResponseBody Employee updateEmp(
	@RequestBody Employee e, @PathVariable String id) {
employeeDS.update(e);
return e;
}
	
@RequestMapping(method=RequestMethod.DELETE, value="/emp/{id}")
public @ResponseBody void removeEmp(@PathVariable String id) {
employeeDS.remove(Long.parseLong(id));
}

@RequestBody批注在addEmp()updateEmp()方法中使用。 它接受HTTP请求正文,并尝试使用已注册的HttpMessageConverter将其转换为对象类。 在下一节中,您将使用RestTemplate与这些服务进行通信。

使用RestTemplate与REST服务进行通信

“使用Spring 3构建RESTful Web服务”介绍了如何使用CURL和REST客户端来测试REST服务。 在编程级别,通常使用Jakarta Commons的HttpClient来这样做(但这不在本文的讨论范围之内)。 您还可以使用称为RestTemplate的Spring REST客户端。 它在概念上类似于Spring中的其他模板类,例如JdbcTemplateJmsTemplate

RestTemplate还使用HttpMessageConverter 。 您可以在请求中传递对象类,然后让转换器处理映射。

配置RestTemplate

清单10显示了RestTemplate的配置。 它还使用前面介绍的三个转换器。

清单10.配置RestTemplate
<bean id="restTemplate" 
class="org.springframework.web.client.RestTemplate">
<property name="messageConverters">
	<list>
	<ref bean="marshallingConverter" />
	<ref bean="atomConverter"  />
	<ref bean="jsonConverter" />
	</list>
</property>
</bean>

本文中的示例仅使用一些可简化服务器之间通信的方法。 RestTemplate支持其他方法,包括:

  • exchange :使用请求正文执行某些HTTP方法并获取响应。
  • getForObject :执行HTTP GET方法并将响应作为对象获取。
  • postForObject :使用特定请求正文执行HTTP POST方法。
  • put :使用特定的请求正文执行HTTP PUT方法。
  • delete :对某个URI执行HTTP DELETE方法。

代码样本

以下代码示例有助于说明如何使用RestTemplate 。 有关所用API的详细说明,请参阅RestTemplate API。

清单11显示了如何向请求添加标头,然后调用请求。 使用MarshallingHttpMessageConverter可以获取响应并将其转换为类型化的类。 您可以使用不同的媒体类型来测试其他表示形式。

清单11.请求XML表示
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_XML);
HttpEntity<String> entity = new HttpEntity<String>(headers);
ResponseEntity<EmployeeList> response = restTemplate.exchange(
"http://localhost:8080/rest/service/emps", 
HttpMethod.GET, entity, EmployeeList.class);
EmployeeListingemployees = response.getBody();
// handle the employees

清单12显示了如何将新员工过帐到服务器。 服务器端服务addEmp()可以接受媒体类型为application / xml和application / json的数据。

清单12.发布新员工
Employee newEmp = new Employee(99, "guest", "guest@ibm.com");
HttpEntity<Employee> entity = new HttpEntity<Employee>(newEmp);
ResponseEntity<Employee> response = restTemplate.postForEntity(
"http://localhost:8080/rest/service/emp", entity, Employee.class);
Employee e = response.getBody();
// handle the employee

清单13显示了如何放置经过修改的员工来更新原始员工。 它还显示了可以在请求URI中用作占位符( {id} )的功能。

清单13.更新员工的PUT
Employee newEmp = new Employee(99, "guest99", "guest99@ibm.com");
HttpEntity<Employee> entity = new HttpEntity<Employee>(newEmp);
restTemplate.put(
	"http://localhost:8080/rest/service/emp/{id}", entity, "99");

清单14显示了如何删除现有雇员。

清单14.删除现有员工
restTemplate.delete(
	"http://localhost:8080/rest/service/emp/{id}", "99");

摘要

在本文中,您了解了Spring 3中引入的HttpMessageConverter 。它为多种表示形式提供了客户端和服务器端支持。 使用提供的源代码 ,您可以探索本文中的HttpMessageConverter的实现与“使用Spring 3构建RESTful Web服务”中的使用ContentNegotiatingViewResolver的实现之间的区别。


翻译自: https://www.ibm.com/developerworks/web/library/wa-restful/index.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值