spring boot中 requestBody里面的json数据重新写入到request里

最近在使用spring security框架,发现一个问题,就是我通过request.getInputStream()方法读取了请求体中的json数据后,就发现在controller控制器层中无法再次获取到参数,会出现报错

I/O error while reading input message; nested exception is java.io.IOException: Stream closed

{
    "timestamp": "2019-07-07T03:19:44.674+0000",
    "status": 400,
    "error": "Bad Request",
    "message": "I/O error while reading input message; nested exception is java.io.IOException: Stream closed",
    "path": "/test1"
}

这是因为request body请求体中的数据已经读取过一次了,所以无法第二次读取。如果能在读取后,直接在将数据写入到request body中,那么我们在控制器层就可以再次读取。

方法如下:

自定义一个request包装器WebHttpServletRequestWrapper ,继承RequestReaderHttpServletRequestWrapper这个类

该类源码如下:

package com.company.request;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;

/**
 * 过滤器
 * @author lichuang
 */
public class WebHttpServletRequestWrapper extends HttpServletRequestWrapper {

    private final byte[] body;

    public WebHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        body = HttpRequestUtil.getBodyContent(request).getBytes(Charset.forName("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 readListener) {

            }
        };
    }
}
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;

/**
 * 工具类
 *
 * @author lichuang
 */
public class HttpRequestUtil {

    public static String getBodyContent(HttpServletRequest request) throws IOException {
        StringBuilder sb = new StringBuilder();
        try (InputStream inputStream = request.getInputStream();
             BufferedReader reader = new BufferedReader(
                     new InputStreamReader(inputStream, Charset.forName("UTF-8")))) {
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return sb.toString();
    }
}

我们再自定义一个过滤器WebServletRequestReplacedFilter

package com.company.request;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * 过滤器
 * @author lichuang
 */
public class WebServletRequestReplacedFilter implements Filter{

    @Override
    public void destroy() {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        ServletRequest requestWrapper = null;
        if (request instanceof HttpServletRequest) {
            requestWrapper = new WebHttpServletRequestWrapper((HttpServletRequest) request);
        }
        //获取请求中的流如何,将取出来的字符串,再次转换成流,然后把它放入到新request对象中。
        // 在chain.doFiler方法中传递新的request对象
        if(requestWrapper == null) {
            chain.doFilter(request, response);
        } else {
            chain.doFilter(requestWrapper, response);
        }
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {

    }
}

最后再将这个过滤器在Configuration中注册下

    @Bean
    public FilterRegistrationBean httpServletRequestReplacedRegistration() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new WebServletRequestReplacedFilter());
        registration.addUrlPatterns("/*");
        registration.addInitParameter("paramName", "paramValue");
        registration.setName("httpServletRequestReplacedFilter");
        registration.setOrder(1);
        return registration;
    }

当我们再次访问时,发现Controller中能正常获取到参数值了。

需要注意的是,注册这个过滤器的时候需要先加载到spring容器中,否则,可能出现filter不被调用的情况。

可以通过Bean的Order值来决定Bean初始化顺序。这个值越小,则越靠前进行实例化。极端情况下,取值Ordered.HIGHEST_PRECEDENCE,则最先进行实例化。

你可能感兴趣的:(SpringMVC,SpringBoot)