问题
当controller的接口返回字符串,在swaggerui中测试时,发现返回都是问号,比如”?????id 100 ???????“,这是由于字符编码问题导致
例如:
responseentity.status(httpstatus.not_found)
.body(string.format("未找到相应id %d 的记录", id));网上解决方案
现有的两种解决方案:
- 第一种,针对单独接口,在requestmapping里设置 produces = {"text/plain;charset=utf-8"}
- 第二种,统一在mvc配置类中,通过修改stringhttpmessageconverter默认配置,部分代码(ps,该代码从别处拷贝而来):
@configuration
@enablewebmvc
public class mymvcconfig implements webmvcconfigurer {
@bean
public httpmessageconverter<string> responsebodystringconverter() {
stringhttpmessageconverter converter = new stringhttpmessageconverter(standardcharsets.utf_8);
return converter;
}
@override
public void configuremessageconverters(list<httpmessageconverter<?>> converters){
converters.add(responsebodystringconverter());
}
}是由于默认的编码是”standardcharsets.iso_8859_1“导致,是通过重写”configuremessageconverters“方法来设置utf-8编码来解决。
也就是第二种,坑了我,也许是我使用不当?
新解决方案
通过研究源码,找到了新的解决思路:
因为通过重写”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" rel="external nofollow" >
* 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
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论