Spring中HttpMessageConveter概念

522f60a1d5aa3bb8e292c5ea83a75351.jpeg

概念

HttpMessageConverter主要功能在于Java对象和Json Xml等Http消息格式之前的来回转换. 来看HttpMessageConverter在Spring中如何使与Http的通信变更更优雅

首先启用SpringMvc会自动注册一箩筐HttpMessageConverter

@EnableWebMvc
@Configuration
@ComponentScan({ "org.springautowired.web" })
public class WebConfig implements WebMvcConfigurer {
    ...
}

默认,有如下组件被注册:

ByteArrayHttpMessageConverter – 转换字节数组
StringHttpMessageConverter – 转换 String,支持 text/html等MIME Type
FormHttpMessageConverter – 转换 form data 与 a MultiValueMap<String, String>,支持 application/x-www-form-urlencoded.
Jaxb2RootElementHttpMessageConverter – 转换 Java objects 与 XML, 支持 application/xml (classpath下有JAXB2)
MappingJackson2HttpMessageConverter – 转换 JSON , 支持 application/json (classpath下有Jackson 2)
MappingJacksonHttpMessageConverter – 转换 JSON , 支持 application/json (classpath下有Jackson)
AtomFeedHttpMessageConverter – 转换 Atom feeds (classpath下有Rome)
RssChannelHttpMessageConverter – 转换 RSS feeds (classpath下有Rome)

每个具体的HttpMessageConverter都实际关联了一个或几个媒体类型( MIMEType)

Http协议

说到http协议,在这里需要提下Http协议头中的几个 header 

Accept 表示当前http请求希望返回的数据格式,或者说支持的数据格式(我想要什么格式) 

Content-type 表示当前请求中的数据是什么格式(我给的是什么格式) 当请求发送到SpringMvc的服务端时,恰恰是利用了Http协议的这一约定来进行逻辑处理

处理逻辑

一个请求到SpringMvc的Controller时,Spring会利用Content-Type来判断使用哪个HttpMessageConverter来读取并转成Java Object 当请求处理完成时,如果是用 @ResponseBody标注的Controller方法,Spring会通过当前http request头中的Accept来判断使用哪个HttpMessageConverter来转换并回写数据到Http reponse的body中

从http请求中读取消息

@RequestBody用于 Controller方法中的参数前时,表示需要将Http请求中的body转成当前的Java实体对象,如何转,是利用上面提到的http协议头中的 Content-Type ,找到恰当的HttpMessageConverter,完成转换.

@RequestMapping(method=RequestMethod.PUT, value="/book/{id}")
public @ResponseBody void updateFoo(
  @RequestBody Book book, @PathVariable String id) {
    bookService.update(book);
}

假如这样请求:

curl -i -X PUT -H "Content-Type: application/json" 
-d '{"id":"1","name":"Killing Commendatore"}' http://localhost:8080/spring-case/book/1

当Spring发现请求头是 application/json,它会通过某种规则( ContentNegotation)来找到 MappingJackson2HttpMessageConverter 从而把Http请求的body转成 Book对象

向Http响应中回写消息

@ResponseBody用于Controller方法时,表示需要SpringMvc把方法的返回值序列化后回写到http response的body中,当然也是利用 Http request中的Accept,请求想要什么就给它什么格式

@RequestMapping(method=RequestMethod.GET, value="/book/{id}")
public @ResponseBody 
Book
 getOne(@PathVariable String id) {
    return bookService.getOne(id);

}

发一个带Accept的请求 

curl --header "Accept: application/json"  http://localhost:8080/spring-case/book/1

会得到如下结果:

{
    "id": 1,
    "name": "Killing Commendatore"
}

上面大致说的是HttpMessageConverter在SpringMvc server-side的作用,此外在 RestTemplate中也同样有使用. 更多RestTemplate介绍可以翻看之前的一篇文章RestTemplate

获取数据

作为Client端,想要得到什么格式的原始数据,只要设置好Http Accept header就好.

String URI = BASE_URI + "book/{id}";
    RestTemplate restTemplate = new RestTemplate();
    //设置需要用到的MessageConverter
    restTemplate.setMessageConverters(Lists.newArrayList(new MarshallingHttpMessageConverter(new XStreamMarshaller())));
    HttpHeaders headers = new HttpHeaders();
    //如果想得到xml设置MediaType.APPLICATION_XML;如果想得到json就设置MediaType.APPLICATION_JSON
    headers.setAccept(Arrays.asList(MediaType.APPLICATION_XML));
    HttpEntity<String> entity = new HttpEntity<String>(headers);
    ResponseEntity<Book> response = 
      restTemplate.exchange(URI, HttpMethod.GET, entity, Book.class,"1");
    Book book = response.getBody();

发送数据

在发送数据的时候,也较简单,只要传入Java Ojbect 把想要发送的数据格式设置到 Http header Content-Type中就可以.

String URI = BASE_URI + "book/{id}";
    RestTemplate restTemplate = new RestTemplate();
    restTemplate.setMessageConverters(getMessageConverters());
    Book resource = new Book(4, "Norwegian Wood");
    HttpHeaders headers = new HttpHeaders();
    headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
    //这里指定想要发送数据的格式
    headers.setContentType((MediaType.APPLICATION_XML));
    HttpEntity<Book> entity = new HttpEntity<Book>(resource, headers);
    ResponseEntity<Book> response = restTemplate.exchange(
      URI, HttpMethod.PUT, entity, Book.class, resource.getId());

关于HttpMessageConverter的表现形式,大概就是这样,总体来说,设计理念符合Spring的一贯做法,屏蔽了http通信当中的各种格式转换细节,在Spring中,我们只需要直接跟Java Object打交道就好了,至于Http请求在进来之前是什么格式,想要什么格式,对于业务逻辑来讲,不需要关心这些细节. 当然,要让这一切完美无误的执行,不管是服务端还是客户端,都需要准确的设置Http消息的Accept和Content-Type.不按这个规则来,往往得不到期望的结果.然而我并不认为这损害了应用的容错性,恰恰相反,每一个发出的http请求,都要准确的设置各种协议头,这是必要的,也是对Http协议规范遵守,毕竟,那些Accept是text/html而又期望得到json格式的http请求,终归是没办法好好玩的.

本篇大体介绍了如何使用,下篇着重说下HttpMessageConverter在底层代码是如何工作的. 

Thx all

b6fd382943740a20aa6433f691a90bbb.jpeg

SpringAutowired

一个有用的公众号

4313676f7143c16ab1767ea7efb23055.jpeg

长按,识别二维码,加关注

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值