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远程调用的文档:

https://blog.csdn.net/qq_43437874/article/details/122235580

在其中重点提到了从Content-Type中来获取得到媒体类型

posted @ 2023-04-15 17:40  写的代码很烂  阅读(379)  评论(0编辑  收藏  举报