SpringBoot内容协商(简单使用、源码解读、默认Converters、自定义Converters)

目录

  • 1. 内容协商
    • 1.1 简单使用
    • 1.2 源码解读
    • 1.3 WebMvcAutoConfiguration提供几种默认HttpMessageConverters
    • 1.4 自定义HttpMessageConverter支持yaml格式输出

1. 内容协商

1.1 简单使用

一套系统适配多端数据返回

SpringBoot内容协商(简单使用、源码解读、默认Converters、自定义Converters)_第1张图片

  • 基于请求头内容协商:(默认开启)
    1. 客户端向服务端发送请求,携带HTTP标准的Accept请求头application/json(默认支持,因为web场景导入了jackson处理的包jackson-core)、application/xml(需要手动设置)
    2. 服务端根据客户端请求头期望的数据类型进行动态返回
  • 基于请求参数内容协商:(需要手动开启)
    1. 发送请求: GET /get-user?format=json(默认支持)
    2. 匹配到@GetMapping(“/get-user”)
    3. 根据参数协商,返回json类型数据
    4. 发送请求: GET /get-user?format=xml, 返回xml类型数据(需要手动设置)

开启xml支持

  1. 先添加pom.xml依赖


    com.fasterxml.jackson.dataformat
    jackson-dataformat-xml

  1. 在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参数,和返回结果如下:
SpringBoot内容协商(简单使用、源码解读、默认Converters、自定义Converters)_第2张图片

通过请求参数,返回结果如下:
SpringBoot内容协商(简单使用、源码解读、默认Converters、自定义Converters)_第3张图片

1.2 源码解读

  • @ResponseBody由HttpMessageConverter处理:标注了@ResponseBody的返回值,将会由支持它的HttpMessageConverter写给浏览器
  1. 如果controller方法的返回值标注了@ResponseBody注解

    1. 请求进来先来到DispatcherServlet的doDispatch()进行处理
    2. 找到一个HandlerAdapter适配器。利用适配器执行目标方法
    3. RequestMappingHandlerAdapter通过调用invokeHandlerMethod()来执行目标方法
    4. 目标方法执行之前,准备好两个东西
      1. HandlerMethodArgumentResolver:参数解析器,确定目标方法每个参数值
      2. HandlerMethodReturnValueHandler:返回值处理器,确定目标方法的返回值该怎么处理
    5. RequestMappingHandlerAdapter里面的invokeAndHandle()真正执行目标方法
    6. 目标方法执行完成,会返回返回值对象
    7. 找到一个合适的返回值处理器HandlerMethodReturnValueHandler
    8. 最终找到RequestResponseBodyMethodProcessor能处理标注了 @ResponseBody注解的方法
    9. RequestResponseBodyMethodProcessor调用writeWithMessageConverters, 利用MessageConverter把返回值写出去
  2. HttpMessageConverter会先进行内容协商

    1. 遍历所有的MessageConverter看谁支持这种内容类型的数据

    2. 默认MessageConverter有以下
      SpringBoot内容协商(简单使用、源码解读、默认Converters、自定义Converters)_第4张图片

    3. 最终因为要json,所以MappingJackson2HttpMessageConverter支持写出json

    4. 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格式输出

  1. 添加依赖,用于对Object进行转换
        
            com.fasterxml.jackson.dataformat
            jackson-dataformat-yaml
        
  1. 编写一个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 {

    //把对象转成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);
        }

    }
}
 
  
  1. 在application.properties中,新增媒体类型
# 新增一种媒体类型
spring.mvc.contentnegotiation.media-types.yaml=text/yaml
  1. 通过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> converters) {
        converters.add(new MyYamlHttpMessageConverter());
    }
}
  1. 测试。访问http://localhost:8080/get-user?type=yaml。效果如下:
    SpringBoot内容协商(简单使用、源码解读、默认Converters、自定义Converters)_第5张图片

你可能感兴趣的:(#,SpringBoot,spring,boot,内容协商使用,内容协商源码,默认Converters,自定义Converters)