Spring Boot 自定义 HttpMessageConverter 导致 Swagger 文档无法访问的解决方案
问题背景
在 Spring Boot 项目中,我们经常需要自定义 HttpMessageConverter
来处理 JSON 序列化,比如将 Long
类型转换为 String
以避免前端 JavaScript 的精度丢失问题。然而,当我们直接覆盖默认的 MappingJackson2HttpMessageConverter
时,可能会导致 Swagger 文档无法正常访问,返回空白页面或 JSON 解析错误。
问题复现
以下是一个典型的自定义 HttpMessageConverter
配置:
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(BigInteger.class, ToStringSerializer.instance);
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
objectMapper.registerModule(simpleModule);
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
jackson2HttpMessageConverter.setObjectMapper(objectMapper);
converters.add(0, jackson2HttpMessageConverter); // 添加到首位
}
问题现象:
-
访问
http://localhost:8080/swagger-ui.html
时,页面无法加载或显示异常。 -
直接访问
http://localhost:8080/v3/api-docs/default
时,返回的 JSON 数据可能不符合 Swagger 的预期格式。
问题原因
-
覆盖默认转换器
Spring Boot 默认会注册MappingJackson2HttpMessageConverter
,用于 JSON 序列化。当我们手动添加自定义转换器并放在首位(converters.add(0, ...)
),会导致 Swagger 使用的默认转换器被跳过。 -
ObjectMapper 配置冲突
Swagger 依赖特定的ObjectMapper
配置来生成 API 文档。如果我们修改了ObjectMapper
(如Long
转String
),可能会破坏 Swagger 的 JSON 结构,导致文档无法正确渲染。
解决方案
方案 1:仅修改现有的 Jackson 转换器(推荐)
避免覆盖默认转换器,而是找到已存在的 MappingJackson2HttpMessageConverter
并修改其 ObjectMapper
:
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
for (HttpMessageConverter<?> converter : converters) {
if (converter instanceof MappingJackson2HttpMessageConverter) {
ObjectMapper objectMapper = ((MappingJackson2HttpMessageConverter) converter).getObjectMapper();
configureObjectMapper(objectMapper);
return;
}
}
// 如果没找到,再添加新的转换器
addNewJacksonConverter(converters);
}
private void configureObjectMapper(ObjectMapper objectMapper) {
SimpleModule module = new SimpleModule();
module.addSerializer(BigInteger.class, ToStringSerializer.instance);
module.addSerializer(Long.class, ToStringSerializer.instance);
module.addSerializer(Long.TYPE, ToStringSerializer.instance);
objectMapper.registerModule(module);
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
}
private void addNewJacksonConverter(List<HttpMessageConverter<?>> converters) {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
configureObjectMapper(objectMapper);
converter.setObjectMapper(objectMapper);
converters.add(converter);
}
方案 2:动态判断请求路径,Swagger 请求走默认转换器
如果必须添加新的 HttpMessageConverter
,可以动态判断请求路径,让 Swagger 请求仍然使用默认转换器:
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
ObjectMapper customObjectMapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(BigInteger.class, ToStringSerializer.instance);
module.addSerializer(Long.class, ToStringSerializer.instance);
module.addSerializer(Long.TYPE, ToStringSerializer.instance);
customObjectMapper.registerModule(module);
customObjectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
MappingJackson2HttpMessageConverter customConverter = new MappingJackson2HttpMessageConverter(customObjectMapper) {
@Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
// 如果是 Swagger 请求,则不使用自定义转换器
ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attrs != null) {
String path = attrs.getRequest().getRequestURI();
if (path.startsWith("/v3/api-docs") || path.startsWith("/swagger-ui")) {
return false; // 让默认转换器处理
}
}
return super.canWrite(clazz, mediaType);
}
};
converters.add(0, customConverter);
}
方案对比
方案 | 优点 | 缺点 |
---|---|---|
修改现有转换器 | 兼容性最好,不影响 Swagger | 需要确保默认转换器存在 |
动态路径判断 | 灵活控制不同请求的转换逻辑 | 需要依赖 RequestContextHolder |
总结
-
推荐使用方案 1(修改现有转换器),因为它不会破坏 Spring Boot 的默认行为,兼容性更好。
-
如果必须新增转换器,可以使用 方案 2(动态路径判断),确保 Swagger 仍然能正常工作。
-
最终目标是 让 Swagger 使用默认的
ObjectMapper
,而业务接口使用自定义的 JSON 序列化规则。
这样,既能解决前端 Long
精度问题,又能保证 Swagger 文档正常访问! 🚀
📌 欢迎在评论区讨论你的解决方案!
🔗 相关技术:Spring Boot、Swagger、HttpMessageConverter、ObjectMapper