写在前面

一直以来使用的接口文档方案都是Knife4j。此次编写新项目,鉴于springfox和Swagger2规范都已经停止维护,所以选择拥抱springdoc+OpenAPI3规范。knife4j也已更新版本,本项目基于Spring Boot2.7.x版本。
忠告:一定要认真看官方文档

问题描述

项目中定义了实体类属性的时间格式为LocalDateTime,如果不做任何处理后端会向前端返回数组格式,而不是我们想要的时间格式。

1
2
3
4
5
// 创建时间
private LocalDateTime createTime;

// 更新时间
private LocalDateTime updateTime;

pyifoQy

像这种情况,以前的项目也遇到过,所以是轻松解决。这里说一下两种解决方案,推荐使用的是第二种。

第一种:
遇到这个问题是因为后端向前端发送的时间是一个LocalDateTime对象,我们只需要把该对象转成时间戳在发送即可。

在定义LocalDateTime类型的属性上上加上注解,对日期进行格式化

1
2
3
4
5
6
7
// 创建时间
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;

// 更新时间
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;

但这种方式,需要在每个时间属性都加上该注解,使用麻烦,耦合度高

第二种:
通过重写WebMvcConfigurationSupport里的消息转换器方法实现(全局),统一对日期类型进行格式处理

具体实现步骤:

  • 提供对象转换器JacksonObjectMapper,基于Jackson进行Java对象到json数据的转换
  • 在WebMvcConfig配置类中扩展Spring mvc的消息转换器,在此消息转换器中使用提供的对象转换器进行Java对象到json数据的转换
    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
    /**
    * 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象
    * 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]
    * 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]
    */
    public class JacksonObjectMapper extends ObjectMapper {

    public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";

    public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm";

    public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";

    public JacksonObjectMapper() {
    super();
    // 收到未知属性时不报异常
    this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);

    // 反序列化时,属性不存在的兼容处理
    this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

    SimpleModule simpleModule = new SimpleModule()
    .addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
    .addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
    .addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))
    .addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
    .addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
    .addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));

    // 注册功能模块 例如,可以添加自定义序列化器和反序列化器
    this.registerModule(simpleModule);
    }
    }

WebMvcConfig配置类里重写消息转换器

dfacba

在我重写消息转换器后导致了knife4j文档请求异常的出现,起初我并未发现该问题,因为在使用Postman进行接口测试,并没有打开文档。当我发现该问题,对比代码版本的变动后,我意识到可能是消息转换器导致了该问题的出现。

FLzPBWE

可以明显看到请求/v3/apis-docs路径返回的是Base64编码,就是因为没有正确返回json数据导致文档显示失败

kvNroB

解决方案

由于之前的项目也是使用的knife4j(Swagger2),也重写过消息转换器,并未出现该问题。当我一筹莫展,反复修改的情况下,突然想起我用的knife4j是新版的。于是乎,我打开了knife4j的官网……

DhcYdM

没错,就是这样的,打开官网看一眼FAQ就可以解决了
ps:不要学我,用新版都不先看一眼文档(其实看了,就看了新版依赖坐标)

hZjQKE

在查看GitHub的issues后,成功解决该问题。就是需要在拓展的自定义消息转换器前注册ByteArrayHttpMessageConverter,一定要注意顺序,不然还是会请求异常

zMyeZiGU

bsCPKVG