一次项目依赖SpringBoot版本做升级后,接口返回响应头Content-Type的值由application/json;charset=UTF-8变成了application/json,导致前端展示出现乱码。
经过对Spring MVC源码一番研究后发现,请求完成输出结果时会使用消息转换器HttpMessageConverter转换数据。我们项目使用的是SpringBoot,MappingJackson2HttpMessageConverter是springboot中默认的Json消息转换器。
问题就出现在MappingJackson2HttpMessageConverter的父类AbstractJackson2HttpMessageConverter中,在Spring5.2以前的版本,AbstractJackson2HttpMessageConverter类中的DEFAULT_CHARSET属性会有一个默认值。
javaDEFAULT_CHARSET = StandardCharsets.UTF_8;
而在Spring5.2以后的版本,AbstractJackson2HttpMessageConverter类中默认字符集去掉了,DEFAULT_CHARSET被设置为了null。
在请求结果返回的时候,AbstractHttpMessageConverter中方法addDefaultHeaders会构建header。
javaprotected 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 许可协议。转载请注明出处!