在自定义filter中使用自定义的HttpServletRequestWrapper后,出现中文乱码

在自定义filter中使用自定义的HttpServletRequestWrapper后,出现中文乱码

 

1、项目使用的环境

tomcat  8.5.34

Springboot  1.5.17

 

2、使用背景和问题描述

在自定义的过滤器Myfilter中,需要从request body 中获取参数,因此使用自定义的类MyHttpServletRequestWrapper继承HttpServletRequestWrapper来封装一下request,以确保Myfilter之后的处理中能从request中获取到请求参数。但是发现封装了request之后,request中获取到的中文乱码了。以下是相关代码。

Myfilter

@Order(Integer.MIN_VALUE)
public class MyFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) {}

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        ServletRequest requestWrapper = servletRequest;
        if (servletRequest instanceof HttpServletRequest) {
            HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
            if (!isFormDataRequest(httpServletRequest)) {
                requestWrapper = new MyHttpServletRequestWrapper(httpServletRequest);
            }

            // 从request body 中获取参数(省略)
        }
        chain.doFilter(requestWrapper, response);
    }
    
    @Override
    public void destroy() {}

    private boolean isFormDataRequest(HttpServletRequest httpServletRequest) {
        String contentType = httpServletRequest.getContentType();
        return Objects.nonNull(contentType) && (contentType.contains("application/x-www-form-urlencoded")
                || contentType.contains("multipart/form-data")) && "POST".equalsIgnoreCase(httpServletRequest.getMethod());
    }
}

MyHttpServletRequestWrapper

public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper {

    private final byte[] body;

    public MyHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        body = StreamUtil.readBytes(request.getReader(), "UTF-8");
    }
    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream bais = new ByteArrayInputStream(body);
        return new ServletInputStream() {
            @Override
            public int read() throws IOException {
                return bais.read();
            }
            @Override
            public boolean isFinished() {
                return false;
            }
            @Override
            public boolean isReady() {
                return false;
            }
            @Override
            public void setReadListener(ReadListener arg0) {}
        };
    }

RequestUtils

@Slf4j
public class RequestUtils {

    /**
     * 获取请求体
     */
    public static String getRequestBody(HttpServletRequest request) {
        String body = null;
        try {
            body = StreamUtils.copyToString(request.getInputStream(),Charset.forName("utf-8"));
        } catch (Exception e) {
            log.error("RequestUtils getRequestBody has an error:{}", e);
        }
        return body;
    }

    public static Object getRequestParam(String body, String param) {
        if (StringUtils.isBlank(body)) {
            return null;
        }

        Object obj = null;
        try {
            JSONObject jsonObject = JSON.parseObject(body);
            obj = jsonObject.get(param);
        } catch (Exception e) {
            log.info("RequestUtils getRequestParam has an error.body:{}.error:{}", body, e);
        }
        return obj;
    }

}

 

3、原因分析

查看源码可知,springboot 有个默认的编码过滤器(OrderedCharacterEncodingFilter)被设置为最先执行,但是由于Myfilter的order也设置为了最小值,导致其执行时间早于OrderedCharacterEncodingFilter

因为目前的浏览器通常不设置字符编码,即使在HTML页面或表单中指定了字符编码,因此需要OrderedCharacterEncodingFilter为请求指定字符编码

同时,如果请求尚未指定编码,则此过滤器可以应用其设置的编码,或者在任何情况下强制使用此过滤器设置的编码(当forceEncoding =true”时(此为默认值))。在后一种情况下,该编码也将作为默认的响应编码。

而且http协议默认的编码是iso-8859-1,因此直接使用 utf-8 去读取数据将导致中文乱码。

OrderedCharacterEncodingFilter源码如下:

public class OrderedCharacterEncodingFilter extends CharacterEncodingFilter implements Ordered {
    private int order = -2147483648;

    public OrderedCharacterEncodingFilter() {
    }

    public int getOrder() {
        return this.order;
    }

    public void setOrder(int order) {
        this.order = order;
    }
}
CharacterEncodingFilter部分源码如下
public class CharacterEncodingFilter extends OncePerRequestFilter {
    private String encoding;
    private boolean forceRequestEncoding;
    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);
    }
}

4、解决办法

(1)将Myfilter的执行顺序设置在OrderedCharacterEncodingFilter之后执行,如设置 @Order(Integer.MIN_VALUE +1) 或 @Order(-1) 

(2)设置Myfilter中request的编码,具体如下

@Order(Integer.MIN_VALUE)
public class MyFilter implements Filter {
    private final HttpEncodingProperties properties;

    public MyFilter(HttpEncodingProperties properties) {
        this.properties = properties;
    }

    @Override
    public void init(FilterConfig filterConfig) {}

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        servletRequest.setCharacterEncoding(properties.getCharset().name());
        ServletRequest requestWrapper = servletRequest;
        if (servletRequest instanceof HttpServletRequest) {
            HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
            if (!isFormDataRequest(httpServletRequest)) {
                requestWrapper = new MyHttpServletRequestWrapper(httpServletRequest);
            }

            // 从request body 中获取参数(省略)
        }
        chain.doFilter(requestWrapper, response);
    }

    @Override
    public void destroy() {}

    private boolean isFormDataRequest(HttpServletRequest httpServletRequest) {
        String contentType = httpServletRequest.getContentType();
        return Objects.nonNull(contentType) && (contentType.contains("application/x-www-form-urlencoded")
                || contentType.contains("multipart/form-data")) && "POST".equalsIgnoreCase(httpServletRequest.getMethod());
    }
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值