SpringMVC中的字符编码问题
字符编码问题
一、背景
最近项目中在使用feign接口调用中产生了乱码问题,所以总结下这里产生乱码的原因。
如下所示:
Feign远程调用过程中出现中文乱码问题
正确的文件名称应该是:
二、排查思路
2.1、查看idea默认编码方式
发现都是UTF-8编码方式,继续向下来进行排查。
2.2、查看接口代码
2.3、查看linux编码
发现也是中文,所以可能存在问题的地方是第二个。
三、解决思路
因为从idea中编码中没有找到问题,所以只有在接口中存在问题,尝试来进行修复。
3.1、修改远程调用编码
指定响应的编码方式
如下所示:
四、SpringMVC对字符编码的配置
4.1、字符编码自动配置类HttpEncodingAutoConfiguration
直接来看自动配置类对字符编码的配置。org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration
源码如下所示:
@Configuration(proxyBeanMethods = false)
// 配置server中的配置
@EnableConfigurationProperties(ServerProperties.class)
// servlet环境
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
// 字符编码类
@ConditionalOnClass(CharacterEncodingFilter.class)
// 字符编码的配置是在这里来进行配置的。默认也是开启的
@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
private final Encoding properties;
public HttpEncodingAutoConfiguration(ServerProperties properties) {
this.properties = properties.getServlet().getEncoding();
}
// 配置过滤器
@Bean
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
// 设置字符编码
filter.setEncoding(this.properties.getCharset().name());
// 强制request编码
filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));
// 强制response编码
filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));
return filter;
}
@Bean
public LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
return new LocaleCharsetMappingsCustomizer(this.properties);
}
static class LocaleCharsetMappingsCustomizer
implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {
private final Encoding properties;
LocaleCharsetMappingsCustomizer(Encoding properties) {
this.properties = properties;
}
// 配置映射路径
@Override
public void customize(ConfigurableServletWebServerFactory factory) {
if (this.properties.getMapping() != null) {
factory.setLocaleCharsetMappings(this.properties.getMapping());
}
}
@Override
public int getOrder() {
return 0;
}
}
}
通过上面的代码,可以看出springmvc所有request的配置是通过
spring.http.encoding
来进行配置的。所以就有必要来对这个配置类的属性来做了解。
4.2、配置类中属性说明
直接参考:org.springframework.boot.web.servlet.server.Encoding
public class Encoding {
/**
* Default HTTP encoding for Servlet applications.
*/
// 采用的模仿编码方式
public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
/**
* Charset of HTTP requests and responses. Added to the "Content-Type" header if not
* set explicitly.
*/
// 针对request和response默认设置的字符编码
private Charset charset = DEFAULT_CHARSET;
/**
* Whether to force the encoding to the configured charset on HTTP requests and
* responses.
*/
// 是否强制对HTTP请求和响应上配置的字符集进行编码
private Boolean force;
/**
* Whether to force the encoding to the configured charset on HTTP requests. Defaults
* to true when "force" has not been specified.
*/
// 是否强制对HTTP请求上配置的字符集进行编码。未指定“force”时默认为true
private Boolean forceRequest;
/**
* Whether to force the encoding to the configured charset on HTTP responses.
*/
// 是否强制对HTTP响应上配置的字符集进行编码
private Boolean forceResponse;
/**
* Locale in which to encode mapping.
*/
// 要在其中编码映射的区域设置
private Map<Locale, Charset> mapping;
// ......
}
即配置文件如下所示:
spring.http.encoding.charset=UTF-8
spring.http.encoding.force=true
参考官方文档中的配置
https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
4.3、过滤器中设置源码
然后进入到org.springframework.web.filter.CharacterEncodingFilter#doFilterInternal方法来
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// 获取得到默认编码方式。默认为UTF-8
String encoding = getEncoding();
if (encoding != null) {
// 两个设置条件:1、forceRequestEncoding是否为true;2、利用request获取得到默认编码
if (isForceRequestEncoding() || request.getCharacterEncoding() == null) {
request.setCharacterEncoding(encoding);
}
// 是否设置强制响应编码。默认值是false
// 默认为false
if (isForceResponseEncoding()) {
response.setCharacterEncoding(encoding);
}
}
filterChain.doFilter(request, response);
}
终于在这里找到了原因。原因就是这里的响应没有设置为中文编码。
五、Tomcat编码设置
另外web窗口 tomcat可以配置
# Character encoding to use to decode the URI.
server.tomcat.uri-encoding=UTF-8
六、总结
1、排查问题的时候,就考虑到多种情况的发生,并逐一进行验证。
2、最终的字符编码配置如下所示:
spring:
http:
encoding:
charset: UTF-8
force: true
3、应该在@RequestMapping中的consumes属性和produces属性中来指定当前接口能够消费什么类型,能够处理哪些种类的媒体数据。
参考一下feign远程调用的文档:
在其中重点提到了从Content-Type中来获取得到媒体类型