一次Post請求@RequestBody接收字串中文亂碼問題
技術標籤:開發中踩的坑javaspringspring boot亂碼
如圖所示,在一次開發中接收Json字串使用@RequestBody接收,傳入的中文出現了亂碼問題
解決過程
一、對配置檔案定義http的編碼
問題尚未解決
#編碼格式
spring.http.encoding.force=true
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
server.tomcat.uri-encoding=UTF-8
二、寫一個字元編碼配置類
@Bean
public HttpMessageConverter< String> responseBodyStringConverter() {
StringHttpMessageConverter converter = new StringHttpMessageConverter(StandardCharsets.UTF_8);
return converter;
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters){
converters. add(responseBodyStringConverter());
}
三、設定請求接收字元型別
針對單獨介面,在RequestMapping裡設定 produces = {"application/json;charset=UTF-8"}
此方法依然無法解決問題
四、解決
實際上在解決的途中,我也參考了之前寫過的一些專案配置,發現並無特殊之處
其他專案配置下都能正常接收Json字串的中文並不會出現亂碼
剎那之間我想起了如果不是本身配置問題那會不會出現在IDEA工具上呢
其實這個想法比較清奇,因為之前做一個人臉識別專案時由於對方提供的SDK包是GBK編碼
所以我對專案的Project Encoding設定為GBK編碼
將它改回UTF-8,問題解決。。。
五、新的解決思路
連結: link.
因為通過重寫”configureMessageConverters“方法後,會導致一些其他問題
比如,統一處理異常的ExceptionAdviceHandler不工作,還導致Controller介面不支援檔案下載
比如:
//解決中文檔名的亂碼問題
String utf8 = StandardCharsets.UTF_8.name();
try {
downloadFileName = URLEncoder.encode(downloadFileName, utf8);
} catch (UnsupportedEncodingException e) {
//
}
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename* = " + utf8 + "''" + downloadFileName)
.body(new UrlResource(downloadFile.toURI()));
並且呼叫下載介面時,會報406錯誤和異常”No converter for [class org.springframework.core.io.UrlResource]” ,意思是不支援 “application/octet-stream“的轉換,見鬼了,通過測試,禁用掉WebMvcConfigurer的重寫,下載功能就ok了,但是會重新有編碼問題。
最終通過研究原始碼,找到了根源,這是由於設定了自己的converter導致預設的其他converters不會再被初始化新增導致,參見WebMvcConfigurationSupport的程式碼:
protected final List<HttpMessageConverter<?>> getMessageConverters() {
if (this.messageConverters == null) {
this.messageConverters = new ArrayList();
this.configureMessageConverters(this.messageConverters);
if (this.messageConverters.isEmpty()) {
this.addDefaultHttpMessageConverters(this.messageConverters);
}
this.extendMessageConverters(this.messageConverters);
}
return this.messageConverters;
}
所以基於這個程式碼,我們則應該重寫extendMessageConverters方法來達到目的,最終的程式碼是:
@Bean
public HttpMessageConverter<String> responseBodyStringConverter() {
StringHttpMessageConverter converter = new StringHttpMessageConverter(StandardCharsets.UTF_8);
return converter;
}
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
List<StringHttpMessageConverter> stringHttpMessageConverters = converters.stream()
.filter(converter -> converter.getClass().equals(StringHttpMessageConverter.class))
.map(converter -> (StringHttpMessageConverter) converter)
.collect(Collectors.toList());
if (stringHttpMessageConverters.isEmpty()) {
converters.add(responseBodyStringConverter());
} else {
stringHttpMessageConverters.forEach(converter -> converter.setDefaultCharset(StandardCharsets.UTF_8));
}
}
六、JSON格式的編碼探討
這裡僅處理介面直接返回字串的問題,而對於處理JSON返回,這是因為JSON返回由MappingJackson2HttpMessageConverter來控制:
protected JsonEncoding getJsonEncoding(@Nullable MediaType contentType) {
if (contentType != null && contentType.getCharset() != null) {
Charset charset = contentType.getCharset();
for (JsonEncoding encoding : JsonEncoding.values()) {
if (charset.name().equals(encoding.getJavaName())) {
return encoding;
}
}
}
return JsonEncoding.UTF8;
}
所以對於返回JSON物件,無需處理,且已經提供了預設的UTF-8編碼,因為當預設沒有設定MediaType的編碼格式時,則會使用該預設的UTF-8編碼。
並且MediaType中針對JSON的編碼有如下解釋:
/**
* A String equivalent of {@link MediaType#APPLICATION_JSON_UTF8}.
* @deprecated as of 5.2 in favor of {@link #APPLICATION_JSON_VALUE}
* since major browsers like Chrome
* <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=438464">
* now comply with the specification</a> and interpret correctly UTF-8 special
* characters without requiring a {@code charset=UTF-8} parameter.
*/
@Deprecated
public static final String APPLICATION_JSON_UTF8_VALUE = "application/json;charset=UTF-8";
PS:org.springframework.boot:spring-boot-starter-web:jar:2.2.1.RELEASE