目录
- 1. 内容协商
- 1.1 简单使用
- 1.2 源码解读
- 1.3 WebMvcAutoConfiguration提供几种默认HttpMessageConverters
- 1.4 自定义HttpMessageConverter支持yaml格式输出
1. 内容协商
1.1 简单使用
一套系统适配多端数据返回
- 基于请求头内容协商:(默认开启)
- 客户端向服务端发送请求,携带HTTP标准的Accept请求头application/json(默认支持,因为web场景导入了jackson处理的包jackson-core)、application/xml(需要手动设置)
- 服务端根据客户端请求头期望的数据类型进行动态返回
- 基于请求参数内容协商:(需要手动开启)
- 发送请求: GET /get-user?format=json(默认支持)
- 匹配到@GetMapping(“/get-user”)
- 根据参数协商,返回json类型数据
- 发送请求: GET /get-user?format=xml, 返回xml类型数据(需要手动设置)
开启xml支持
- 先添加pom.xml依赖
<!-- 添加了依赖前,默认返回json,添加依赖后,默认返回xml -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
- 在Bean类上添加xml支持注解
@JacksonXmlRootElement // 其实不加这个也可以
public class User {
......省略部分......
}
开启基于请求参数内容协商。application.properties参数设置如下
# 开启基于请求参数的内容协商功能。 默认为false功能不开启
spring.mvc.contentnegotiation.favor-parameter=true
# 指定内容协商时使用的参数名。默认是format
spring.mvc.contentnegotiation.parameter-name=type
示例:RestController如下:
package com.hh.springboot3test.controller;
import com.hh.springboot3test.bean.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ControllerTest {
@GetMapping("/get-user")
public User user() {
return new User("jim");
}
}
通过header头传递accept参数,和返回结果如下:
通过请求参数,返回结果如下:
1.2 源码解读
- @ResponseBody由HttpMessageConverter处理:标注了@ResponseBody的返回值,将会由支持它的HttpMessageConverter写给浏览器
-
如果controller方法的返回值标注了@ResponseBody注解
- 请求进来先来到DispatcherServlet的doDispatch()进行处理
- 找到一个HandlerAdapter适配器。利用适配器执行目标方法
- RequestMappingHandlerAdapter通过调用invokeHandlerMethod()来执行目标方法
- 目标方法执行之前,准备好两个东西
- HandlerMethodArgumentResolver:参数解析器,确定目标方法每个参数值
- HandlerMethodReturnValueHandler:返回值处理器,确定目标方法的返回值该怎么处理
- RequestMappingHandlerAdapter里面的invokeAndHandle()真正执行目标方法
- 目标方法执行完成,会返回返回值对象
- 找到一个合适的返回值处理器HandlerMethodReturnValueHandler
- 最终找到RequestResponseBodyMethodProcessor能处理标注了 @ResponseBody注解的方法
- RequestResponseBodyMethodProcessor调用writeWithMessageConverters, 利用MessageConverter把返回值写出去
-
HttpMessageConverter会先进行内容协商
-
遍历所有的MessageConverter看谁支持这种内容类型的数据
-
默认MessageConverter有以下
-
最终因为要json,所以MappingJackson2HttpMessageConverter支持写出json
-
jackson用ObjectMapper把对象写出去
-
1.3 WebMvcAutoConfiguration提供几种默认HttpMessageConverters
● EnableWebMvcConfiguration -> DelegatingWebMvcConfiguration -> WebMvcConfigurationSupport -> addDefaultHttpMessageConverters添加了默认的MessageConverter。如下:
- ByteArrayHttpMessageConverter: 支持字节数据读写
- StringHttpMessageConverter: 支持字符串读写
- ResourceHttpMessageConverter:支持资源读写
- ResourceRegionHttpMessageConverter: 支持分区资源写出
- AllEncompassingFormHttpMessageConverter:支持表单xml/json读写
- MappingJackson2HttpMessageConverter: 支持请求响应体Json读写
系统提供默认的MessageConverter功能有限,仅用于json或者普通返回数据。额外增加新的内容协商功能,必须增加新的HttpMessageConverter
1.4 自定义HttpMessageConverter支持yaml格式输出
- 添加依赖,用于对Object进行转换
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
</dependency>
- 编写一个YamlHttpMessageConverter
package com.hh.springboot3test.component;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
public class MyYamlHttpMessageConverter extends AbstractHttpMessageConverter<Object> {
//把对象转成yaml
private ObjectMapper objectMapper = null;
public MyYamlHttpMessageConverter() {
// 告诉SpringBoot这个MessageConverter支持哪种媒体类型
super(new MediaType("text", "yaml", Charset.forName("UTF-8")));
YAMLFactory factory = new YAMLFactory()
.disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER);
this.objectMapper = new ObjectMapper(factory);
}
@Override
protected boolean supports(Class<?> clazz) {
// 只要是对象类型,不是基本类型,则都支持
return true;
}
// @RequestBody。对接收的yaml格式参数进行解析,这里不处理
@Override
protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
return null;
}
// @ResponseBody。把对象以yaml格式传输出去
@Override
protected void writeInternal(Object methodReturnValue, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
// try-with写法,自动关流
try (OutputStream os = outputMessage.getBody()) {
this.objectMapper.writeValue(os, methodReturnValue);
}
}
}
- 在application.properties中,新增媒体类型
# 新增一种媒体类型
spring.mvc.contentnegotiation.media-types.yaml=text/yaml
- 通过WebMvcConfigurer,将MyYamlHttpMessageConverter添加到conveters
package com.hh.springboot3test.config;
import com.hh.springboot3test.component.MyYamlHttpMessageConverter;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
@Configuration
public class WebConfig implements WebMvcConfigurer {
// 添加一个能把对象转为yaml的messageConverter
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new MyYamlHttpMessageConverter());
}
}
- 测试。访问http://localhost:8080/get-user?type=yaml。效果如下: