编辑
2023-08-09
后端技术
0
请注意,本文编写于 283 天前,最后修改于 246 天前,其中某些信息可能已经过时。

一次项目依赖SpringBoot版本做升级后,接口返回响应头Content-Type的值由application/json;charset=UTF-8变成了application/json,导致前端展示出现乱码。

经过对Spring MVC源码一番研究后发现,请求完成输出结果时会使用消息转换器HttpMessageConverter转换数据。我们项目使用的是SpringBoot,MappingJackson2HttpMessageConverter是springboot中默认的Json消息转换器。

问题就出现在MappingJackson2HttpMessageConverter的父类AbstractJackson2HttpMessageConverter中,在Spring5.2以前的版本,AbstractJackson2HttpMessageConverter类中的DEFAULT_CHARSET属性会有一个默认值。

java
DEFAULT_CHARSET = StandardCharsets.UTF_8;

而在Spring5.2以后的版本,AbstractJackson2HttpMessageConverter类中默认字符集去掉了,DEFAULT_CHARSET被设置为了null。

在请求结果返回的时候,AbstractHttpMessageConverter中方法addDefaultHeaders会构建header。

java
protected void addDefaultHeaders(HttpHeaders headers, T t, @Nullable MediaType contentType) throws IOException { if (headers.getContentType() == null) { MediaType contentTypeToUse = contentType; if (contentType != null && !contentType.isWildcardType() && !contentType.isWildcardSubtype()) { if (MediaType.APPLICATION_OCTET_STREAM.equals(contentType)) { MediaType mediaType = this.getDefaultContentType(t); contentTypeToUse = mediaType != null ? mediaType : contentType; } } else { contentTypeToUse = this.getDefaultContentType(t); } if (contentTypeToUse != null) { if (contentTypeToUse.getCharset() == null) { Charset defaultCharset = this.getDefaultCharset(); if (defaultCharset != null) { // 当defaultCharset不为null时,会创建一个新的MediaType加到响应头中 contentTypeToUse = new MediaType(contentTypeToUse, defaultCharset); } } headers.setContentType(contentTypeToUse); } } if (headers.getContentLength() < 0L && !headers.containsKey("Transfer-Encoding")) { Long contentLength = this.getContentLength(t, headers.getContentType()); if (contentLength != null) { headers.setContentLength(contentLength); } } }

在源码中可以看到,当消息转换器中defaultCharset不为null时,会创建一个新的MediaType加到响应头中,那么新老版本就出现了差别,5.2之前版本Content-Type的值会是application/json;charset=UTF-8,5.2之后的版本会是application/json。为什么会去掉?在MediaType类中APPLICATION_JSON_UTF8_VALUE有解释:由于Chrome等主流浏览器现在已经无需charset=UTF-8参数就能正确解释UTF-8字符。

那么如何解决这个问题呢?让Spring5.2版本之后请求响应头中Content-Type的值也是application/json;charset=UTF-8。

遍历RequestMappingHandlerAdapter里的消息转换器,判断如果是MappingJackson2HttpMessageConverter就设置默认字符集为'UTF-8'。

java
@Configuration public class WebConfig { @Autowired private RequestMappingHandlerAdapter requestMappingHandlerAdapter; @PostConstruct public void init() { requestMappingHandlerAdapter.getMessageConverters().forEach(c -> { if (c instanceof MappingJackson2HttpMessageConverter) { ((MappingJackson2HttpMessageConverter) c).setDefaultCharset(StandardCharsets.UTF_8); } }); } }

本文作者:whitebear

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!