getInputStream () has already been called for this request แก้ยังไง

เมื่อจำเป็นต้องใช้คำสั่งนี้จาก Class HttpServletRequest เพื่อหาค่า requestBody เช่น getReader , getInputStream และพบ error

java.lang.IllegalStateException: getReader() has already been called for this request
java.lang.IllegalStateException: getInputStream () has already been called for this request

จะแก้อย่างไร ให้เราสร้าง Filter สำหรับทำ CachingRequestBodyFilter

@Component
public class CachingRequestBodyFilter extends GenericFilterBean {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest currentRequest = (HttpServletRequest) servletRequest;
        MultiReadHttpServletRequest wrappedRequest = new MultiReadHttpServletRequest(currentRequest);
        chain.doFilter(wrappedRequest, servletResponse);
    }
}

และสร้าง Class MultiReadHttpServletRequest สำหรับเก็บ cache ของ HttpServletRequest ใน class นี้ support servlet 3.0

public class MultiReadHttpServletRequest extends HttpServletRequestWrapper {
    private ByteArrayOutputStream cachedBytes;

    public MultiReadHttpServletRequest(HttpServletRequest request) {
        super(request);
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        if (cachedBytes == null)
            cacheInputStream();

        return new CachedServletInputStream();
    }

    @Override
    public BufferedReader getReader() throws IOException{
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    private void cacheInputStream() throws IOException {
    /* Cache the inputstream in order to read it multiple times. For
     * convenience, I use apache.commons IOUtils
     */

        cachedBytes = new ByteArrayOutputStream();
        IOUtils.copy(super.getInputStream(), cachedBytes);
    }

    /* An inputstream which reads the cached request body */
    public class CachedServletInputStream extends ServletInputStream {
        private ByteArrayInputStream input;

        public CachedServletInputStream() {
      /* create a new input stream from the cached request body */
            input = new ByteArrayInputStream(cachedBytes.toByteArray());
        }

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

        @Override
        public boolean isFinished() {
            return input.available() == 0;
        }

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

        @Override
        public void setReadListener(ReadListener listener) {
            throw new RuntimeException("Not implemented");
        }
    }
}

หลังจากนั้นเราสามารถเรียกคำสั่งในข้างต้น กี่ครั้งก็ได้ ดังนี้ ยกตัวอย่าง จะหาค่า input ที่เป็นแบบ raw json post string

MultiReadHttpServletRequest multiReadRequest = new MultiReadHttpServletRequest((HttpServletRequest) request);
InputStream inputStream = multiReadRequest.getInputStream();
String rawJsonString = IOUtils.toString(inputStream, "UTF-8");

reference https://stackoverflow.com/questions/47605079/exceptionhandler-how-to-get-raw-json-post-from-httpservletrequest?noredirect=1#comment82190546_47605079

Related posts:

This entry was posted in java, spring boot. Bookmark the permalink.