- 根据客户端接收能力不同,返回不同媒体类型的数据

默认协商规则
基于请求头内容协商:(默认开启)
- 客户端向服务端发送请求,携带HTTP标准的Accept请求头,例如:application/json 、 text/xml 、 text/yaml
- 服务端根据客户端请求头期望的数据类型进⾏动态返回
基于请求参数内容协商:(需要开启)
- 发送请求 GET /projects/spring-boot?format=json
- 匹配到 @GetMapping("/projects/spring-boot")
- 根据参数协商,优先返回 json 类型数据【需要开启参数匹配设置】
- 发送请求 GET /projects/spring-boot?format=xml,优先返回 xml 类型数据
bash
# 开启基于请求参数的内容协商功能。 默认此功能不开启
spring.mvc.contentnegotiation.favor-parameter=true
# 指定内容协商时请求路径里使⽤的参数名。默认是 format
spring.mvc.contentnegotiation.parameter-name=type
bash
http://localhost:8080/test/person?format=json
http://localhost:8080/test/person?format=xml
内容协商原理
- 判断当前响应头中是否已经有确定的媒体类型。MediaType
- 获取客户端Accept请求头字段,判断支持接收的内容类型
- contentNegotiationManager类,内容协商管理器,默认使用基于请求头的策略
- HeaderContentNegotiationStrategy 确定客户端可以接收的内容类型


- 遍历循环所有当前系统的 MessageConverter,看谁支持操作返回对象

- 找到支持操作返回格式的converter,把converter支持的媒体类型统计出来
- 比如json、xml等等,进行内容协商的最佳匹配媒体类型
- 用支持将对象转为最佳匹配媒体类型的converter进行转化
自定义MessageConverter
- MessageConverter作用
- 使用注解@ResponseBody的Controller方法响应数据出去,通过调用 RequestResponseBodyMethodProcessor 处理
- Processor 处理方法通过 MessageConverter 处理返回值
- 所有 MessageConverter 合起来可以支持各种媒体类型数据的操作(读、写)
- 内容协商找到最终的 messageConverter
- 自定义MessageConverter
- 配置类中添加一个 WebMvcConfigurer
java
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
//加入自定义的Converter
converters.add(new MyMessageConverter());
}
}
}
java
public class MyMessageConverter implements HttpMessageConverter<Person> {
@Override
public boolean canRead(Class<?> clazz, MediaType mediaType) {
return false;
}
@Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
//匹配是否是person类型
return clazz.isAssignableFrom(Person.class);
}
/**
* 服务器要统计所有MessageConverter都能写出哪些内容类型
*
* application/x-my
* @return
*/
@Override
public List<MediaType> getSupportedMediaTypes() {
return MediaType.parseMediaTypes("application/x-my");
}
@Override
public Person read(Class<? extends Person> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
return null;
}
@Override
public void write(Person person, MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
//自定义协议数据的写出格式
String data = person.getUserName()+";"+person.getAge()+";"+person.getBirth();
//写出去
OutputStream body = outputMessage.getBody();
body.write(data.getBytes());
}
}
- 自定义后效果


- 此时的配置只支持匹配请求头是application/x-my,不支持匹配请求参数format=my

- 适配请求参数format=自定义
- 配置类实现:配置类中WebMvcConfigurer重写configureContentNegotiation()
- 第一种重写方法:覆盖默认协商策略strategies和媒体类型mediaTypes,全自定义
- 第二种重写方法:只是追加媒体类型mediaTypes
java
/**
* 自定义内容协商策略
* @param configurer
*/
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
Map<String, MediaType> mediaTypes = new HashMap<>();
mediaTypes.put("json",MediaType.APPLICATION_JSON);
mediaTypes.put("xml",MediaType.APPLICATION_XML);
mediaTypes.put("my",MediaType.parseMediaType("application/x-my"));
//指定支持解析哪些参数对应的哪些媒体类型
//配置匹配参数format的策略
ParameterContentNegotiationStrategy parameterStrategy =
new ParameterContentNegotiationStrategy(mediaTypes);
//自定义匹配的参数,默认为format
//parameterStrategy.setParameterName("ff");
//配置匹配请求头策略,否则就是匹配*/*
HeaderContentNegotiationStrategy headerStrategy =
new HeaderContentNegotiationStrategy();
//此处自定义后将覆盖默认的strategies
configurer.strategies(Arrays.asList(parameterStrategy,headerStrategy));
}
java
/**
* 自定义内容协商策略
* @param configurer
*/
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
//添加自定义的媒体类型
configurer.mediaType("my",MediaType.parseMediaType("application/x-my"));
}
- 注解实现
bash
spring:
mvc:
hiddenmethod:
filter:
enabled: true #restFul的过滤器
contentnegotiation:
favor-parameter: true #开启支持请求参数进行内容协商
media-types:
my: application/x-my #自定义内容协商的媒体类型
