springboot @requestbody的编码问题

最近遇到一个很蛋疼的问题,机器发来http请求,信息都放在body Data里用gb2312编码,然后后台用@RequestBody来接受,这时问题来了,机器发来的请求没有设置content-type,于是默认就是content-type:application/x-www-form-urlencoded,然后spring容器就默认设置CharacterEncoding为utf-8,来解码。更奇怪的是,spring中途还对body使用urlencode。

比方说http body内容是“温度设定值”,最后接受到的是

%ef%bf%bd%c2%b6%ef%bf%bd%ef%bf%bd%e8%b6%a8%d6%b5,用URLDecode.decode(data,"gb2312")解码后是“锟铰讹拷锟借定值”,经过我多次试验,实际过程是这样的,

    public static void main(String[] args) {
        try {
            String data = "温度设定值";
            System.out.println(data);
            data = new String(data.getBytes("gb2312"),"utf-8");
            System.out.println(data);
            data = URLEncoder.encode(data,"utf-8");
            System.out.println(data);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
温度设定值
�¶��趨ֵ
%EF%BF%BD%C2%B6%EF%BF%BD%EF%BF%BD%E8%B6%A8%D6%B5
多次debug后终于发现了spring自作主张对内容进行urlencode,在ServletServerHttpRequest类的

getBodyFromServletRequestParameters方法中

    /**
     * Use {@link javax.servlet.ServletRequest#getParameterMap()} to reconstruct the
     * body of a form 'POST' providing a predictable outcome as opposed to reading
     * from the body, which can fail if any other code has used the ServletRequest
     * to access a parameter, thus causing the input stream to be "consumed".
     */
    private static InputStream getBodyFromServletRequestParameters(HttpServletRequest request) throws IOException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream(1024);
        Writer writer = new OutputStreamWriter(bos, FORM_CHARSET);
 
        Map<String, String[]> form = request.getParameterMap();
        for (Iterator<String> nameIterator = form.keySet().iterator(); nameIterator.hasNext();) {
            String name = nameIterator.next();
            List<String> values = Arrays.asList(form.get(name));
            for (Iterator<String> valueIterator = values.iterator(); valueIterator.hasNext();) {
                String value = valueIterator.next();
                writer.write(URLEncoder.encode(name, FORM_CHARSET));
                if (value != null) {
                    writer.write('=');
                    writer.write(URLEncoder.encode(value, FORM_CHARSET));
                    if (valueIterator.hasNext()) {
                        writer.write('&');
                    }
                }
            }
            if (nameIterator.hasNext()) {
                writer.append('&');
            }
        }
        writer.flush();
 
        return new ByteArrayInputStream(bos.toByteArray());
    }
    protected static final String FORM_CHARSET = "UTF-8";
FORM_CHARSET是静态常量,也就是说是固定的。spring为什么要将@RequestBody接受的数据进行urlencode编码我不得而知,总之我们知道了不管怎样最后都应该用UrlDecode.decode(data,"utf-8")进行解码,然后问题就变成了如何解决spring用utf-8默认解码的问题。

查了很多资料,也试验了很多,最快捷的方法自然是机器传来的信息加上消息头 content-type charset=GB2312,这样tomcat容器就会使用gb2312进行解码了。但是我的提议没有被接受。那么只有强制在这个消息路径里使用

request.setCharacterEncoding("gbk2312")了,但是servlet规定只有在调用request.getParameters()之前设置

characterEncoding才会生效,而spring容器早就不知道在之前做过了多少事情了。我试图在过滤器中设置request编码,但很可惜并不生效,说明spring在过滤器之前就调用了getParameters方法。

@WebFilter(filterName = "encodingFilter", urlPatterns = "/*")
public class MutiCharacterEncodingFilter extends OncePerRequestFilter
在这个问题上我花费了大量时间,几乎绝望了,网上的信息都是说要在getParameters之前设置request编码,这我已经充分了解了,但是你告诉我加在哪儿啊?怎么在spring做出处理之前设置编码。最绝望的就是,你知道解决问题的方法,但却不知道怎么实现。最后我幸运地找到了资料,很可惜并不是我自己独立完成的。

Spring boot 字符集编码
作者跟踪源码,发现CharacterEncodingFilter会调用request.setCharacterEncoding("UTF-8"),于是他写了一个类继承

CharacterEncodingFilter,并在Application中注入它

    @Bean()
    @ConfigurationProperties(prefix = "spring.http.encoding")
    @ConfigurationPropertiesBinding
    public MutiCharacterEncodingFilter mutiCharacterEncodingFilter(){
        MutiCharacterEncodingFilter encodingFilter = new MutiCharacterEncodingFilter();
        encodingFilter.setEncoding(charset);
        encodingFilter.setForceRequestEncoding(forceRequest);
        encodingFilter.setForceResponseEncoding(forceResponse);
        return encodingFilter;
    }
这样自定义的过滤器就会取代CharacterEncodingFilter。request.setCharacterEncoding也就生效了。

这让我对springboot的了解更加得深入,之前都是copy别人的代码,发现有很多用@Bean的方式配置变量,一直只知其然不知其所以然。看来springboot会把@Bean标注的变量替换掉他默认的变量,只要这个变量继承了那个默认变量。而我用

@WebFilter加入的过滤器只是加在这些默认过滤器的后面,而不是替换这些默认过滤器。

至此,乱码问题终于解决


--------------------- 
作者:从心归零 
来源:CSDN 
原文:https://blog.csdn.net/qq_36804701/article/details/81011397 
版权声明:本文为博主原创文章,转载请附上博文链接!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值