返回介绍

16.2.2 使用 HTTP 信息转换器

发布于 2024-08-17 00:45:49 字数 6975 浏览 0 评论 0 收藏 0

消息转换(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)之间的互相转换。
如果 Rome 包在类路径下将会进行注册

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注解的对象间互相读取和写入。
如果 JAXB v2 库在类路径下,将进行注册

MappingJacksonHttpMessageConverter

在JSON和类型化的对象或非类型化的HashMap间互相读取和写入。
如果 Jackson JSON 库在类路径下,将进行注册

MappingJackson2HttpMessageConverter

在JSON和类型化的对象或非类型化的HashMap间互相读取和写入。
如果 Jackson 2 JSON 库在类路径下,将进行注册

MarshallingHttpMessageConverter

使用注入的编排器和解排器(marshaller和unmarshaller)来读入和写入XML。支持的编排器和解排器包括Castor、JAXB2、JIBX、XMLBeans以及Xstream

ResourceHttpMessageConverter

读取或写入Resource

RssChannelHttpMessageConverter

在RSS feed和Rome Channel对象间互相读取或写入。
如果 Rome 库在类路径下,将进行注册

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文