目录

Spring Boot - 内容协商

视频:https://www.bilibili.com/video/BV1Es4y1q7Bf?p=34,p34 - p38。

或者看原理:https://www.bilibili.com/video/BV19K4y1L7MT?p=39,p39 - p42。

# 内容协商

根据不同客户端接受能力的不同,返回不同类型的数据。

简单理解就是浏览器发生请求的时候,告诉后端需要的返回格式是什么,是 XML 或者 JSON,然后后端处理完请求后,返回前先判断自己可以返回什么格式,然后再和浏览器告诉的格式进行对比,找出权重高的格式进行返回。

浏览器通过在请求头的 accept 来告诉后端,如:

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
1

q 代表权重,如果后端支持权重高的则处理成该格式返回。*/* 代表接收任意格式。

后端返回的格式在响应头的 Content-Type 里:

Content-Type: text/html;charset=UTF-8
1

Spring Boot 内置支持内容协商,只需要浏览器传的 Accept 指定需要什么格式,那么 Spring Boot 就返回这个,当然格式不是随便写的。

基于请求头内容协商:(默认开启)

客户端向服务端发送请求,携带 HTTP 标准的 Accept 请求头

Accept: application/json`、text/xml、text/yaml
1

服务端根据客户端 请求头期望的数据类型 进行 动态返回

基于请求参数内容协商:(需要开启)

发送请求 GET /projects/spring-boot?format=json

根据参数协商,优先返回 json 类型数据【需要开启参数匹配设置

发送请求 GET /projects/spring-boot?format=xml,优先返回 XML 类型数据

在 application.yml 开启

spring:
  mvc:
    contentnegotiation:
      favor-parameter: true # 开启基于请求参数的内容协商功能。默认参数名:format。默认此功能不开启
      parameter-name: format1 # 指定内容协商时使用的参数名。默认是 format
1
2
3
4
5

开启后内容协商管理器,就会多了一个 ParameterContentNegotiationStrategy(由 Spring 容器注入)。

Spring Boot 内置 JSON 解析器,但是没有 XML 解析器,因此如果需要返回 XML 格式的,则需要引入依赖:

<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
</dependency>
1
2
3
4

# 原理

内容协商原理:

  1. 判断当前响应头中是否已经有确定的媒体类型 MediaType
  2. 获取客户端(PostMan、浏览器)支持接收的内容类型。(获取客户端 Accept 请求头字段 application/xml)
    • contentNegotiationManager 内容协商管理器 默认使用基于请求头的策略
    • HeaderContentNegotiationStrategy 确定客户端可以接收的内容类型
  3. 遍历循环所有当前系统的 MessageConverter,看谁支持操作这个对象(Person)
  4. 找到支持操作 Person 的 converter,把 converter 支持的媒体类型统计出来
  5. 客户端需要 application/xml,服务端有 10 种 MediaType
  6. 进行内容协商的最佳匹配媒体类型
  7. 用支持将对象转为最佳匹配媒体类型的 converter。调用它进行转化

Spring Boot 内容协商采用策略模式来选择使用哪个策略解析返回格式。

Spring Boot 默认只有 基于请求头内容协商 的解析器,但是当开启 基于请求参数内容协商 后,容器会多一个解析器,这样 Spring Boot 优先使用这个解析器去解析返回格式。

包括还有其他解析器,但是默认只用一个,需要其他的就开启 Spring Boot 提供的解析器,这就是策略模式:提供多个策略,让用户选择一个,如果用户不选,则有默认策略去执行。

# 自定义 MessageConverter

我们可以自己定义协议:application/x-youngkbt

当浏览器请求头携带 Accept: application/x-youngkbt 时,则返回 xx; xx 数据。

  1. @ResponseBody 响应数据出去 调用 RequestResponseBodyMethodProcessor 处理

  2. Processor 处理方法返回值。通过 MessageConverter处理

  3. 所有 MessageConverter 合起来可以支持各种媒体类型数据的操作(读、写)

  4. 内容协商找到最终的 messageConverter

自定义 Converter,需要实现 HttpMessageConverter

public class MyMessageConverter implements HttpMessageConverter<Person> {

    @Override
    public boolean canRead(Class<?> clazz, MediaType mediaType) {
        return false;
    }

    @Override
    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
        return clazz.isAssignableFrom(Person.class);
    }

    /**
     * 服务器要统计所有 MessageConverter 都能写出哪些内容类型
     */
    @Override
    public List<MediaType> getSupportedMediaTypes() {
        return MediaType.parseMediaTypes("application/x-youngkbt");
    }

    @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());
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

注册

@Configuration(proxyBeanMethods = false)
public class WebConfig {
    @Bean
    public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer() {

            @Override
            public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
                converters.add(new MyMessageConverter());
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

用 Postman 发送请求(请求头 Accept:application/x-youngkbt),将返回自定义协议数据的写出。

更新时间: 2024/01/17, 05:48:13
最近更新
01
JVM调优
12-10
02
jenkins
12-10
03
Arthas
12-10
更多文章>