Spring中请求参数、返回参数中文乱码问题

1,138 阅读3分钟

spring中解决中文乱码问题,是CharacterEncodingFilter通过拦截器解决的 我们来看一下这个拦截器的源码。

public class CharacterEncodingFilter extends OncePerRequestFilter {    
    //编码格式
    @Nullable
    private String encoding;
    // 请求参数是否使用encoding属性设置的编码格式
    private boolean forceRequestEncoding;
    // 返回参数是否使用encoding属性设置的编码格式
    private boolean forceResponseEncoding; 

    // 过滤器中会执行的方法
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String encoding = this.getEncoding();
        if (encoding != null) {
        // 设置请求参数的编码格式
            if (this.isForceRequestEncoding() || request.getCharacterEncoding() == null) {
                request.setCharacterEncoding(encoding);
            }
       // 设置返回参数的编码格式
            if (this.isForceResponseEncoding()) {
                response.setCharacterEncoding(encoding);
            }
        }

        filterChain.doFilter(request, response);
    }
}

通过上面的源码可以得出,encoding是没有默认值的,所以需要我们自己指定编码格式,一般前后端约定好就行,通常是utf-8 来看下springMvc中如何指定编码格式。 springMvc项目过滤器需要在web.xml中配置

<filter>
  <filter-name>encodingFilter</filter-name> spring内置的过滤器
  <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
  <init-param>
    <param-name>encoding</param-name>  指定参数的格式为utf-8
    <param-value>UTF8</param-value>
  </init-param>
  <init-param>
    <param-name>forceEncoding</param-name>  请求参数和返回参数都是使用上面设置的编码格式
    <param-value>true</param-value>
  </init-param>
</filter>

有些朋友看到这里可能会很奇怪,CharacterEncodingFilter中根本没有forceEncoding这个属性啊。哈哈,我开始也是一脸懵逼,怎么可以这样设置。后来发现,CharacterEncodingFilter中有下面两个构造方法

// 很明显web.xml中的配置是通过这个构造方法,为三个属性赋值
    public CharacterEncodingFilter(String encoding, boolean forceEncoding) {
        this(encoding, forceEncoding, forceEncoding);
    }

    public CharacterEncodingFilter(String encoding, boolean forceRequestEncoding, boolean forceResponseEncoding) {
        this.forceRequestEncoding = false;
        this.forceResponseEncoding = false;
        Assert.hasLength(encoding, "Encoding must not be empty");
        this.encoding = encoding;
        this.forceRequestEncoding = forceRequestEncoding;
        this.forceResponseEncoding = forceResponseEncoding;
    }

下面再看看springBoot项目中怎样解决参数中文乱码问题吧。 大家应该都知道,不就是在yml或者properties配置文件中加一个配置嘛。对的加如下配置,已yml为例

spring:
  http:
    encoding:
      force: true
      charset: UTF-8
      enabled: true

至于为什么加这个配置就行了,才是我们要弄懂的重点啊。 大家都知道,springBoot的自动装配吧。会在容器启动的时候将很多配置类加载到ioc容器中。 下面我们来看看这个配置类HttpEncodingAutoConfiguration

这里是简化后的源码,只留下了与编码问题相关的

// 表示这是一个配置类,和以前编写的配置文件一样,也可以给容器中添加组件
@Configuration(proxyBeanMethods = false)

// 启动指定类的ConfigurationProperties功能;将配置文件中对应的值和HttpProperties中的属性映射
@EnableConfigurationProperties(HttpProperties.class)


// 判断当前项目有没有这个CharacterEncodingFilter : SpringMVC中进行乱码解决的过滤器
@ConditionalOnClass(CharacterEncodingFilter.class)

public class HttpEncodingAutoConfiguration {

	// 它已经和SpringBoot配置文件中的值进行映射了
	private final HttpProperties.Encoding properties;

	// 只有一个有参构造器的情况下,参数的值就会从容器中拿
	public HttpEncodingAutoConfiguration(HttpProperties properties) {
		this.properties = properties.getEncoding();
	}

	@Bean  //给容器中添加一个组件,这个组件中的某些值需要从properties中获取
	@ConditionalOnMissingBean  //判断容器中没有这个组件
	public CharacterEncodingFilter characterEncodingFilter() {
		CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
		filter.setEncoding(this.properties.getCharset().name());
		filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
		filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
		return filter;
	}

}

@EnableConfigurationProperties注解,会将配置文件中的属性值,映射到HttpProperties类中 至于注解怎样实现这些功能的,暂时不做研究

HttpProperties代码如下

@ConfigurationProperties(prefix = "spring.http")
public class HttpProperties {

	private boolean logRequestDetails;
	
	private final Encoding encoding = new Encoding();

	public boolean isLogRequestDetails() {
		return this.logRequestDetails;
	}

	public void setLogRequestDetails(boolean logRequestDetails) {
		this.logRequestDetails = logRequestDetails;
	}

	public Encoding getEncoding() {
		return this.encoding;
	}

	/**
	 * Configuration properties for http encoding.
	 */
	public static class Encoding {

		public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
		private Charset charset = DEFAULT_CHARSET;
		private Boolean force;
		private Boolean forceRequest;
		private Boolean forceResponse;
		private Map<Locale, Charset> mapping;

		public boolean shouldForce(Type type) {
			Boolean force = (type != Type.REQUEST) ? this.forceResponse : this.forceRequest;
			if (force == null) {
				force = this.force;
			}
			if (force == null) {
				force = (type == Type.REQUEST);
			}
			return force;
		}

		public enum Type {

			REQUEST, RESPONSE

		}

	}

看了HttpProperties中的属性,你应该会发现这些属性就是我们在配置文件中配置的属性。并且默认utf-8. 给这些属性赋值后,再来看看他们怎样与CharacterEncodingFilter关联。

下面这个方法可以找到答案

这个方法在HttpEncodingAutoConfiguration中,对CharacterEncodingFilter进行了实例化

this.properties就是HttpProperties.Encoding。shouldForce是里面一个方法。代码在上面

	@Bean  //给容器中添加一个组件,这个组件中的某些值需要从properties中获取
	@ConditionalOnMissingBean  //判断容器中没有这个组件
	public CharacterEncodingFilter characterEncodingFilter() {
		CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
		filter.setEncoding(this.properties.getCharset().name());
		filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
		filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
		return filter;
	}

好了,这配置的时候不会很懵逼,不知道为什么要这样配置了