@The RequestBody parameter has been read. Why?

Keywords: Programming Java JSON encoding

I don't know if you check the user's allergy. If you don't check, the user belongs to some attack scripts, then our service will be forced! So we first need to read the user's data through the filter for safety verification, which involves an action, that is, we need to read the user's data in the filter for verification, and then release it after passing.

problem

If our data is a get request, it's OK, but if it's a large amount of data, we need to pass the data through post json, which is actually a stream. If we read out the parameters in the filter, and then release them. When we get to the Servlet, @ RequestBody can't get the data, because post json uses stream passing. After the stream is read, it doesn't exist. So we read it in the filter ,@ReqeustBody Naturally, I can't read the data. At the same time, I will report the following error.

  • Read the data in the body in the filter
@WebFilter
@Slf4j
public class CheckUserFilter implements Filter {

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;

        // Read data in filter
        BufferedReader reader = request.getReader();

        StringBuilder sb = new StringBuilder();

        String line;
        while ((line = reader.readLine()) != null) {
            sb.append(line);
        }
        reader.close();

        System.out.println(sb.toString());

        filterChain.doFilter(request, res);
    }
}
  • There is an exception, that is, the content has been read and you cannot call
{	"id":"1",	"username":"bingfeng"}
java.lang.IllegalStateException: UT010003: Cannot call getInputStream(), getReader() already called
	at io.undertow.servlet.spec.HttpServletRequestImpl.getInputStream(HttpServletRequestImpl.java:666)
	at javax.servlet.ServletRequestWrapper.getInputStream(ServletRequestWrapper.java:152)
	at javax.servlet.ServletRequestWrapper.getInputStream(ServletRequestWrapper.java:152)

solve

  • HttpServletRequestWrapper

So what should we do if there is such a problem? Can we save the data through an intermediate variable, and then we can read it all the time, which will solve the problem? Where is it kept? At this time, HttpServletRequestWrapper will come into use.

In fact, you can understand it as a Request wrapper class. There are some methods in Reqeust. We can inherit this class, rewrite the methods in this class, save the parameters in the body in a byte array, and pass the wrapper class in when we release it. Can't we get the parameters all the time?

  • Encapsulate Request class
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {

    private final byte[] body;

    /**
     * Set of all parameters
     */
    private Map<String, String[]> parameterMap;


    public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        BufferedReader reader = request.getReader();
        body = readBytes(reader);
        parameterMap = request.getParameterMap();
    }


    @Override
    public BufferedReader getReader() throws IOException {

        ServletInputStream inputStream = getInputStream();

        if (null == inputStream) {
            return null;
        }

        return new BufferedReader(new InputStreamReader(inputStream));
    }

    @Override
    public Enumeration<String> getParameterNames() {
        Vector<String> vector = new Vector<>(parameterMap.keySet());
        return vector.elements();
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {

        if (body == null) {
            return null;
        }

        final ByteArrayInputStream bais = new ByteArrayInputStream(body);
        return new ServletInputStream() {

            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener listener) {

            }

            @Override
            public int read() throws IOException {
                return bais.read();
            }
        };
    }

    /**
     * Convert to byte array with BufferedReader and character encoding set
     *
     * @param br
     * @return
     * @throws IOException
     */
    private byte[] readBytes(BufferedReader br) throws IOException {
        String str;
        StringBuilder retStr = new StringBuilder();
        while ((str = br.readLine()) != null) {
            retStr.append(str);
        }
        if (StringUtils.isNotBlank(retStr.toString())) {
            return retStr.toString().getBytes(StandardCharsets.UTF_8);
        }
        return null;
    }
}
  • Transform the filter
@WebFilter
@Slf4j
public class CheckUserFilter implements Filter {

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;

        BodyReaderHttpServletRequestWrapper requestWrapper = new BodyReaderHttpServletRequestWrapper(request);

        // Read data from Request's wrapper class
        BufferedReader reader = requestWrapper.getReader();

        StringBuilder sb = new StringBuilder();

        String line;
        while ((line = reader.readLine()) != null) {
            sb.append(line);
        }
        reader.close();

        System.out.println(sb.toString());

        filterChain.doFilter(requestWrapper, res);
    }
}

After this configuration, even if we get the parameters in the filter, the request will arrive at the Servlet.

If the basic knowledge IO is not very solid, the first thing I see is that this problem is really muddled. I also solved it after Baidu. It's really worth recording. Sometimes we will save and output all the requested parameters. If it's post json data at this time, if it's not particularly clear, this problem may also occur.

<p style="text- align:center;font-weight :bold;color:#0e88eb;font- size:20px "> if you are a member of the sun arch, you will not be able to donate</p>

<p style="text- align:center;font-weight :bold;color:#773098;font- size:16px "> please pay attention to more</p>

Posted by wilorichie on Tue, 26 May 2020 19:27:13 -0700