- 作者简介
- 内容提要
- 关于本书
- 路线图
- 代码规范与下载
- 作者在线
- 封面插图简介
- 前言
- 译者序
- 致谢
- 第1部分 Spring 的核心
- 第1章 Spring 之旅
- 第2章 装配 Bean
- 第3章 高级装配
- 第4章 面向切面的 Spring
- 第2部分 Web 中的 Spring
- 第5章 构建 Spring Web 应用程序
- 第6章 渲染 Web 视图
- 第7章 Spring MVC 的高级技术
- 第8章 使用 Spring Web Flow
- 第9章 保护 Web 应用
- 第3部分 后端中的 Spring
- 第10章 通过 Spring 和 JDBC 征服数据库
- 第11章 使用对象-关系映射持久化数据
- 第12章 使用 NoSQL 数据库
- 第13章 缓存数据
- 第14章 保护方法应用
- 第4部分 Spring 集成
- 第15章 使用远程服务
- 第16章 使用 Spring MVC 创建 REST API
- 第17章 Spring消息
- 第18章 使用 WebSocket 和 STOMP 实现消息功能
- 第19章 使用 Spring 发送 Email
- 第20章 使用 JMX 管理 Spring Bean
- 第21章 借助 Spring Boot 简化 Spring 开发
16.2.2 使用 HTTP 信息转换器
消息转换(message conversion)提供了一种更为直接的方式,它能够将控制器产生的数据转换为服务于客户端的表述形式。当使用消息转换功能时,DispatcherServlet不再需要那么麻烦地将模型数据传送到视图中。实际上,这里根本就没有模型,也没有视图,只有控制器产生的数据,以及消息转换器(message converter)转换数据之后所产生的资源表述。
Spring自带了各种各样的转换器,如表16.1所示,这些转换器满足了最常见的将对象转换为表述的需要。
例如,假设客户端通过请求的Accept头信息表明它能接受“application/json”,并且Jackson JSON在类路径下,那么处理方法返回的对象将交给MappingJacksonHttp-MessageConverter,并由它转换为返回客户端的JSON表述形式。另一方面,如果请求的头信息表明客户端想要“text/xml”格式,那么Jaxb2RootElementHttpMessage-Converter将会为客户端产生XML响应。
注意,表16.2中的HTTP信息转换器除了其中的五个以外都是自动注册的,所以要使用它们的话,不需要Spring配置。但是为了支持它们,你需要添加一些库到应用程序的类路径下。例如,如果你想使用MappingJacksonHttpMessageConverter来实现JSON消息和Java对象的互相转换,那么需要将Jackson JSON Processor库添加到类路径中。类似地,如果你想使用Jaxb2RootElementHttpMessageConverter来实现XML消息和Java对象的互相转换,那么需要JAXB库。如果信息是Atom或RSS格式的话,那么Atom-FeedHttpMessageConverter和RssChannelHttpMessageConverter会需要Rome库。
表16.1 Spring提供了多个HTTP信息转换器,用于实现资源表述与各种Java类型之间的互相转换
信息转换器 | 描 述 |
AtomFeedHttpMessageConverter | Rome Feed对象和Atom feed(媒体类型application/atom+xml)之间的互相转换。 |
BufferedImageHttpMessageConverter | BufferedImages与图片二进制数据之间互相转换 |
ByteArrayHttpMessageConverter | 读取/写入字节数组。从所有媒体类型(*/*)中读取,并以application/octet-stream格式写入 |
FormHttpMessageConverter | 将application/x-www-form-urlencoded内容读入到MultiValueMap<String,String>中,也会将MultiValueMap<String,String>写入到application/x-www-form- urlencoded中或将MultiValueMap<String, Object>写入到multipart/form-data中 |
Jaxb2RootElementHttpMessageConverter | 在XML(text/xml或application/xml)和使用JAXB2注解的对象间互相读取和写入。 |
MappingJacksonHttpMessageConverter | 在JSON和类型化的对象或非类型化的HashMap间互相读取和写入。 |
MappingJackson2HttpMessageConverter | 在JSON和类型化的对象或非类型化的HashMap间互相读取和写入。 |
MarshallingHttpMessageConverter | 使用注入的编排器和解排器(marshaller和unmarshaller)来读入和写入XML。支持的编排器和解排器包括Castor、JAXB2、JIBX、XMLBeans以及Xstream |
ResourceHttpMessageConverter | 读取或写入Resource |
RssChannelHttpMessageConverter | 在RSS feed和Rome Channel对象间互相读取或写入。 |
SourceHttpMessageConverter | 在XML和javax.xml.transform.Source对象间互相读取和写入。 |
StringHttpMessageConverter | 将所有媒体类型(*/*)读取为String。将String写入为text/plain |
XmlAwareFormHttpMessageConverter | FormHttpMessageConverter的扩展,使用SourceHttp MessageConverter来支持基于XML的部分 |
你可能已经猜到了,为了支持消息转换,我们需要对Spring MVC的编程模型进行一些小调整。
在响应体中返回资源状态
正常情况下,当处理方法返回Java对象(除String外或View的实现以外)时,这个对象会放在模型中并在视图中渲染使用。但是,如果使用了消息转换功能的话,我们需要告诉Spring跳过正常的模型/视图流程,并使用消息转换器。有不少方式都能做到这一点,但是最简单的方法是为控制器方法添加@ResponseBody注解。
重新看一下程序清单16.1中的spittles()方法,我们可以为其添加@ResponseBody注解,这样就能让Spring将方法返回的List<Spittle>转换为响应体:
@ResponseBody注解会告知Spring,我们要将返回的对象作为资源发送给客户端,并将其转换为客户端可接受的表述形式。更具体地讲,DispatcherServlet将会考虑到请求中Accept头部信息,并查找能够为客户端提供所需表述形式的消息转换器。
举例来讲,假设客户端的Accept头部信息表明它接受“application/json”,并且Jackson JSON库位于应用的类路径下,那么将会选择MappingJacksonHttpMessage-Converter或MappingJackson2HttpMessageConverter(这取决于类路径下是哪个版本的Jackson)。消息转换器会将控制器返回的Spittle列表转换为JSON文档,并将其写入到响应体中。响应大致会如下所示:
Jackson默认会使用反射
注意在默认情况下,Jackson JSON库在将返回的对象转换为JSON资源表述时,会使用反射。对于简单的表述内容来讲,这没有什么问题。但是如果你重构了Java类型,比如添加、移除或重命名属性,那么所产生的JSON也将会发生变化(如果客户端依赖这些属性的话,那客户端有可能会出错)。
但是,我们可以在Java类型上使用Jackson的映射注解,从而改变产生JSON的行为。这样我们就能更多地控制所产生的JSON,从而防止它影响到API或客户端。
Jackson映射注解的内容超出了本书的讨论范围,不过关于这个主题,在http://wiki.fasterxml.com/Jackson-Annotations上有一些有用的文档。
谈及Accept头部信息,请注意getSpitter()的@RequestMapping注解。在这里,我使用了produces属性表明这个方法只处理预期输出为JSON的请求。也就是说,这个方法只会处理Accept头部信息包含“application/json”的请求。其他任何类型的请求,即使它的URL匹配指定的路径并且是GET请求也不会被这个方法处理。这样的请求会被其他的方法来进行处理(如果存在适当方法的话),或者返回客户端HTTP 406(Not Acceptable)响应。
在请求体中接收资源状态
到目前为止,我们只关注了REST端点如何为客户端提供资源。但是REST并不是只读的,REST API也可以接受来自客户端的资源表述。如果要让控制器将客户端发送的JSON和XML转换为它所使用的Java对象,那是非常不方便的。在处理逻辑离开控制器的时候,Spring的消息转换器能够将对象转换为表述——它们能不能在表述传入的时候完成相同的任务呢?
@ResponseBody能够告诉Spring在把数据发送给客户端的时候,要使用某一个消息器,与之类似,@RequestBody也能告诉Spring查找一个消息转换器,将来自客户端的资源表述转换为对象。例如,假设我们需要一种方式将客户端提交的新Spittle保存起来。我们可以按照如下的方式编写控制器方法来处理这种请求:
如果忽略掉注解的话,那saveSpittle()是一个非常简单的方法。它接受一个Spittle对象作为参数,并使用SpittleRepository进行保存,最终返回spittleRepository.save()方法所得到的Spittle对象。
但是,通过使用注解,它会变得更加有意思也更加强大。@RequestMapping表明它只能处理“/spittles”(在类级别的@RequestMapping中进行了声明)的POST请求。POST请求体中预期要包含一个Spittle的资源表述。因为Spittle参数上使用了@RequestBody,所以Spring将会查看请求中的Content-Type头部信息,并查找能够将请求体转换为Spittle的消息转换器。
例如,如果客户端发送的Spittle数据是JSON表述形式,那么Content-Type头部信息可能就会是“application/json”。在这种情况下,DispatcherServlet会查找能够将JSON转换为Java对象的消息转换器。如果Jackson 2库在类路径中,那么MappingJackson2HttpMessageConverter将会担此重任,将JSON表述转换为Spittle,然后传递到saveSpittle()方法中。这个方法还使用了@ResponseBody注解,因此方法返回的Spittle对象将会转换为某种资源表述,发送给客户端。
注意,@RequestMapping有一个consumes属性,我们将其设置为“application/ json”。consumes属性的工作方式类似于produces,不过它会关注请求的Content-Type头部信息。它会告诉Spring这个方法只会处理对“/spittles”的POST请求,并且要求请求的Content-Type头部信息为“application/json”。如果无法满足这些条件的话,会由其他方法(如果存在合适的方法的话)来处理请求。
为控制器默认设置消息转换
当处理请求时,@ResponseBody和@RequestBody是启用消息转换的一种简洁和强大方式。但是,如果你所编写的控制器有多个方法,并且每个方法都需要信息转换功能的话,那么这些注解就会带来一定程度的重复性。
Spring 4.0引入了@RestController注解,能够在这个方面给我们提供帮助。如果在控制器类上使用@RestController来代替@Controller的话,Spring将会为该控制器的所有处理方法应用消息转换功能。我们不必为每个方法都添加@ResponseBody了。我们所定义的SpittleController可能就会如下所示:
程序清单16.3 使用@RestController注解
程序清单16.3的关键点在于代码中此时不包含什么。这两个处理器方法都没有使用@ResponseBody注解,因为控制器使用了@RestController,所以它的方法所返回的对象将会通过消息转换机制,产生客户端所需的资源表述。
到目前为止,我们看到了如何使用Spring MVC编程模型将RESTful资源发布到响应体之中。但是响应除了负载以外还会有其他的内容。头部信息和状态码也能够为客户端提供响应的有用信息。接下来,我们看一下在提供资源的时候,如何填充头部信息和设置状态码。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论