1. 程式人生 > 其它 >一次Post請求@RequestBody接收字串中文亂碼問題

一次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