关于springboot2.3 application/x-www-form-urlencoded;charset=GBK @RequestParam接受参数乱码

springboot项目,版本2.3.5,系统编码是utf-8。但是合作方请求的时候Content-Type是application/x-www-form-urlencoded;charset=GBK,用@RequestParam 接受参数map的时候乱码了。但是HttpServletRequest.getInputStream()中获取的是正常的。

正常HttpServletRequest.getInputStream()获取的结果是:%5B%BA%EC%C9%BD%C9%AD%C1%D6%B6%AF%CE%EF%D4%B0-%B4%F3%C3%C5%C6%B1%5D%C4%CF%BE%A9%BA%EC%C9%BD%C9%AD%C1%D6%B6%AF%CE%EF%D4%B0%B3%C9%C8%CB%C6%B1%B4%F3%C3%C5%C6%B1 ,这样直接urldecode(var,"GBK")就可以了。

但是不管是通过@RequestParam获取map还是通过@RequestBody获取字符串,字符串都变成了:%5B%EF%BF%BD%EF%BF%BD%C9%BD%C9%AD%EF%BF%BD%D6%B6%EF%BF%BD%EF%BF%BD%EF%BF%BD%D4%B0-%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%C6%B1%5D%EF%BF%BD%CF%BE%EF%BF%BD%EF%BF%BD%EF%BF%BD%C9%BD%C9%AD%EF%BF%BD%D6%B6%EF%BF%BD%EF%BF%BD%EF%BF%BD%D4%B0%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%C6%B1%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%C6%B1
这感觉像是又被编码了一次。然后就怎么都解不开了。
有人遇到过这个问题吗?@RequestBody和@RequestParam 是怎么把数据封装到参数里的啊?感觉就是这里面出了问题。

还有个现象:在springboot2.1.4版本的时候,只要设置CharacterEncodingFilter就可以了。

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    CharacterEncodingFilter characterEncodingFilter() {
        CharacterEncodingFilter filter = new CharacterEncodingFilter();
        filter.setEncoding("UTF-8");
        filter.setForceEncoding(true);
        return filter;
    }

这是强制转utf8,但是因为版本升级了,这个配置没有效果。

server:
  servlet:
    encoding:
      charset: UTF-8
      force: true

然后2.3.5版本版本添加上面的配置也没有效果。


搞了一天,终于弄明白了。
springboot 2.3.5中默认的编码是UTF-8,查看Encoding类中方法shouldForce


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;
}

可以看出默认request是强制转成UTF-8的。也就是request.getCharacterEncoding获取的是UTF-8。那么在获取参数的时候就会用UTF-8来解码。

但是如果配置不强制转换的话,则从请求头信息ContentType中的编码格式来转换,比如设置ContentType是application/x-www-form-urlencoded;charset=GBK,则用GBK来转换。


private static String getCharsetFromContentType(String contentType) {

        if (contentType == null) {
            return null;
        }
        int start = contentType.indexOf("charset=");
        if (start < 0) {
            return null;
        }
        String encoding = contentType.substring(start + 8);
        int end = encoding.indexOf(';');
        if (end >= 0) {
            encoding = encoding.substring(0, end);
        }
        encoding = encoding.trim();
        if ((encoding.length() > 2) && (encoding.startsWith("\""))
            && (encoding.endsWith("\""))) {
            encoding = encoding.substring(1, encoding.length() - 1);
        }

        return encoding.trim();
    }

所以这种情况只要修改配置文件就行了,也就是force-request=false

server:
  servlet:
    encoding:
      charset: UTF-8
      force-request: false

如果请求头信息ContentType里没有charset,那么默认还是UTF-8

@RequestMapping(produces = “application/x-www-form-urlencoded;charset=GBK”, method = RequestMethod.POST),这样设置一下?

apringmvc提供了参数解析器来实现参数绑定,对于不同的注解以及contentType,调用不同的实现类!你这种的话,和别人约定好,到底是走json还是不走json,走json的话,可以实现requestBobyAdvise,不走json,可以用自己去实现filter