配套文章“使用Spring 3构建RESTful Web服务”介绍了构建RESTful Web服务的“ Spring方式”。 它还说明了如何使用ContentNegotiatingViewResolver
生成多个表示形式,这是RESTful Web服务的重要功能。 本文介绍了使用HttpMessageConverter
生成多种表示形式的另一种方法,并且本文中的示例演示了如何将RestTemplate
与HttpMessageConverter
一起使用以与服务进行通信。
Spring MVC中的REST支持
本部分概述了支持RESTful Web服务的主要Spring功能或注释。
-
使用
@Controller
批注来注释将成为MVC中的控制器并处理HTTP请求的类。 -
使用
@RequestMapping
批注来注释应处理某些HTTP方法,URI或HTTP标头的函数。 该注释是Spring REST支持的关键。 您更改method
参数以处理其他HTTP方法。例如:
@RequestMapping(method=RequestMethod.GET, value="/emps", headers="Accept=application/xml, application/json")
-
可以使用
@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文档以获取可以注入到方法中的受支持的注释或对象的完整列表。
@Controller
@RequestMapping
@PathVariable
多代表支持
用不同的MIME类型表示相同的资源是RESTful Web服务的重要方面。 通常,您将使用具有不同“接受” HTTP标头的相同URI来获取具有不同表示形式的资源。 您还可以使用其他URI或具有不同请求参数的URI。
“使用Spring 3构建RESTful Web服务”引入了ContentNegotiatingViewResolver
,它可以选择一个不同的视图解析器来处理相同的URI(具有不同的accept标头)。 因此, ContentNegotiatingViewResolver
可用于生成多个表示形式。
还有另一种产生多个表示形式的方法-通过组合HttpMessageConverter
和c@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
批注用于使返回对象( Employee
或EmployeeList
)成为响应主体内容,该内容将由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显示了POST
, PUT
和DELETE
方法的实现。
清单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中的其他模板类,例如JdbcTemplate
和JmsTemplate
。
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
:执行HTTPGET
方法并将响应作为对象获取。 -
postForObject
:使用特定请求正文执行HTTPPOST
方法。 -
put
:使用特定的请求正文执行HTTPPUT
方法。 -
delete
:对某个URI执行HTTPDELETE
方法。
代码样本
以下代码示例有助于说明如何使用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