为Spring添加REST功能

REST的基本原理

RPC是面向服务的,并关注于行为和动作.REST是面向资源的,强调描述应用程序的事物和名词.
为了理解REST是什么,我们将首字母拆分为不同的组成部分来理解:

  • 表述性(Representational)-REST资源实际上可以用各种形式来表述,包括XML,JSON,html,适合资源使用者的任意形式.
  • 状态(State)-当使用REST时,我们更关注资源的状态而不是对资源采取的行为.
  • 转移(Transfer)-Rest涉及转移资源数据.它以某一种表述性形式从一个应用转移到了一个应用.
    跟简介的讲,REST就是将资源的状态以最合适的形式从服务器端转移到客户端(或者反之) .

    Spring是如何支持REST的

spring有以下方式来开发REST资源:

  • 控制器可以处理所有的HTTP方法:GET,POST,DELETE,PUT.
  • 新的@PathVariable注解使得控制器能够处理参数化的URL(将变量输入作为URL的一部分).
  • Spring的表单绑定JSP标签库的<form:form>标签以及新的HiddenHttpMethodFilter使得通过Delete和put提交表单请求成为可能,即便在某些浏览器中不支持这些HTTP方法.
  • 通过使用Spring的视图和视图解析器,资源可以以各种形式进行表述,包括将模型数据表现为XML,JSON,Atom和RSS的新视图实现.
  • 可以使用新的ContentNegotiatingViewResolver来选择最适合客户端的表述.
  • 基于视图的渲染可以使用新的@ResponseBody注解和各种HttpMethodConverter实现来达到.
  • RestTemplate简化了客户端对REST资源的使用.

表述资源

Spring提供了2种方法将资源的java表述形式转换为发送给客户端的表述形式:

  • 基于视图渲染进行协商
  • HTTP消息转换器

    协商资源表述

    Spring的ContentNegotiatingViewResolver是一个特殊的视图解析器,它考虑到了客户端所需要的内容类型.
    它需要配置在Spring应用上下文中:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <bean class="org.springfreamework.web.servlet.view.ContentNegotiatingViewResolver" >
    <property name="mediaTypes">
    <map>
    <entry key="json" value="application/json" />
    <entry key="xml" value="text/xml" />
    <entry key="htm" value="text/html" />
    </map>
    </property>
    <property name="defaultContentType" value="text/html" />
    </bean>
  1. 确定请求的媒体类型.
  2. 找到适合媒体请求类型的最佳视图
    确定请求的媒体类型
    浏览器的Accept并不总是可靠的,默认浏览器总是以用户友好的最佳视图text/html,所以没有办法指定不同的内容类型.

但是ContentNegotiatingViewResolver会首先考虑url的文件扩展名,如果url在结尾处有文件扩展名的话.它将扩展名与mideaTypes中的条目进行匹配,如果找到了匹配项,那么将会使用找到的媒体类型.
通过这种方式,文件扩展名将覆盖Accept头信息中的任何媒体类型.
如果文件扩展名不能匹配任何媒体类型,那么将会使用浏览器请中的Accept的头信息,如果请求中不包含Accept头部信息,那么将使用defaultContentType属性设置的媒体类型.

影响如何选择媒体类型:
有几个选项会影响媒体默认选择策略:

  • favorPathExtension属性设置为false,将会使得ContentNegotiatingViewResolver忽略url路径的扩展名.
  • 将JAF(Java Activation Framework)添加到类路径下将会使得ContentNegotiatingViewResolver除了使用mediaTypes属性中的条目以外,在由路径扩展名确定媒体类型时还会还会借助JAF .
  • 如果你将favorParameter属性设置为true,并且请求中包含名为format参数,那么format参数的值将会与mediaTypes进行匹配(另外,参数名可以通过设置parameterName属性来选择).
  • 将ignoreAcceptHeader设置为true,将忽略Accept信息.

所以我们一般按下面的配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<bean id="contentNegotiationManager"
class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="favorPathExtension" value="true"/>
<property name="favorParameter" value="false"/>
<property name="parameterName" value="format"/>
<property name="ignoreAcceptHeader" value="false"/>
<property name="mediaTypes">
<value>
do=text/html
json=application/json
</value>
</property>
<property name="defaultContentType" value="text/html"/>
</bean>

查找视图

ContentNegotiatingViewResolver会委托视图解析器来查找最适合客户端的视图,如果没有特别指明,将会使用应用程序中的所有视图解析器,但可以通过viewResolvers属性明确声明它委托的视图解析器列表.

使用HTTP信息转换器

典型的Spring MVC控制器方法在结束时会将一些信息放在模型中,然后到达一个视图来为用户渲染这些数据.
但是,当控制器的工作是产生资源表述的时候,有一种更直接的方法可以绕过模型和数据.在这种风格的处理器方法中,控制器返回的对象将自动转化为适合客户端的表述行式.
要使用这种技术,需要将@ResponseBody注解添加到控制器处理方法上.

在响应体中返回资源状态

正常情况下,当处理方法返回Java对象时(除String外),这个对象会放在模型中,并在视图中渲染使用.但是如果处理器方法使用了@ResponseBody,那表明HTTP信息转换器机制会发挥作用.并将返回的对象转换为客户端需要的任意格式.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public interface HttpMessageConverter<T> {
// Indicate whether the given class and media type can be read by this converter.
boolean canRead(Class<?> clazz, MediaType mediaType);
// Indicate whether the given class and media type can be written by this converter.
boolean canWrite(Class<?> clazz, MediaType mediaType);
// Return the list of MediaType objects supported by this converter.
List<MediaType> getSupportedMediaTypes();
// Read an object of the given type from the given input message, and returns it.
T read(Class<T> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException;
// Write an given object to the given output message.
void write(T t, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException;
}

框架提供了主要media类型的具体实现,并使用客户端的RestTemplate默认注册,在服务器端使用AnnotationMethodHandlerAdapter

StringHttpMessageConverter
StringHttpMessageConverter实现类可以从HTTP请求和响应中读写String类型。默认地,这个转换器支持所有的文本media类型(*/*),并使用text/plainContent-Type来写。

FormHttpMessageConverter
FormHttpMessageConverter实现类可以读写HTTP请求和响应中任何格式的数据。默认地,这个转换器读写的media类型是application/x-www-form-urlencoded。格式化数据的读写都在MultiValueMap<String, String>的集合中。

ByteArrayHttpMessageConverter
一个HttpMessageConverter的实现可以从HTTP请求和响应中读写字节数组。默认地,这个转换器支持所有默认的media类型(*/*)并使用application/octet-stream的Content-Type写。这个可以通过设置supportedMediaTypes属性重写,并可重写getContentType(byte[])

MarshallingHttpMessageConverter
HttpMessageConverter的实现类可以使用org.springframework.oxm包中的MarshallerUnmarshaller抽象类读写XML。这个转换器在使用前需要一个MarshallerUnmarshaller。这些可以通过构造器或bean属性注入。默认地这个构造器支持( text/xml)( application/xml)

MappingJackson2HttpMessageConverter(or MappingJacksonHttpMessageConverter with Jackson 1.x)
HttpMessageConverter实现类可以使用Jackson’s ObjectMapper读写JSON。JSON映射可以通过Jackson提供的注解按需定制化。当需要将来的控制时,一个通用的ObjectMapper可以通过ObjectMapper属性注入,这发生在通用的JSON序列化或反序列化需要提供给指定类型的时候。默认地这个转换器支持(application/json).

SourceHttpMessageConverter
HttpMessageConverter实现类可以读写来自HTTP请求和响应的javax.xml.transform.Source。只支持DOMSource,SAXSource和StreamSource。默认地,这个转换器支持 ( text/xml) 和 ( application/xml)。

BufferedImageHttpMessageConverter
HttpMessageConverter实现类可以读写HTTP请求和响应的java.awt.image.BufferedImage。这个转换器读写由Java I/O API支持的media类型.

了解RestTemplate的操作

对于样板式的客户端代码,我们可以使用RestTemplate去简化代码.

  • get

    1
    2
    3
    4
    public Spittle[] retrieveSpittlesForSpitter(String userName){
    RestTemplate restTemplate = new RestTemplate();
    return restTemplate.getForObject("http://localhost:8080/Spitter/spitters/{spitter}/spittles",spittle[].class,userName);
    }
  • post

    1
    2
    3
    4
    public Spitter postSpitterForObject(Spitter spitter){
    RestTemplate restTemplate = new RestTemplate();
    return restTemplate.postForObject("http://localhost:8080/Spitter/spitters",spitter,Spitter.class);
    }

delete 和 post方法就不一一说明了.用的不多.

热评文章