0

I'm using ResponseEntityExceptionHandler in a Spring Boot-based RESTFul application, to capture errors in a single place and output a consistent error payload.

Everything works as expected. A class that extends ResponseEntityExceptionHandler can override handleMethodArgumentNotValid in order to "react" on a client sending a message that doesn't pass the validation. Currently, validation is triggered by annotating the Request Body with the @Valid annotation.

Everything works as expected. If a client POST a JSON document which doesn't validate, the code in the handleMethodArgumentNotValid is executed correctly.

Now, for monitoring purposes, I would like to log the invalid JSON. Obviously, the original InputStream is closed.

I solved a similar problem recently by sublcassing MappingJackson2HttpMessageConverter, but in the case in the link I needed to "hook" into the validation process and thrown a custom exception containing the invalid JSON document.

Any pointers?

Community
  • 1
  • 1
Luciano
  • 847
  • 1
  • 11
  • 23

1 Answers1

0

You can try this Filter that wraps original HttpServletRequest with implementation that stores InputStreams`s data internally after it is read by container. Here is the code:

public class RequestCachingRequestWrapperFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        filterChain.doFilter(new RequestCachingRequestWrapper(request), response);
    }

    public class RequestCachingRequestWrapper extends HttpServletRequestWrapper {

        private final ByteArrayOutputStream bos = new ByteArrayOutputStream();
        private final ServletInputStream inputStream;
        private BufferedReader reader;

        private RequestCachingRequestWrapper(HttpServletRequest request) throws IOException {
            super(request);
            this.inputStream = new RequestCachingInputStream(request.getInputStream());
        }

        public ByteArrayOutputStream getCache() {
            return this.bos;
        }

        @Override
        public ServletInputStream getInputStream() throws IOException {
            return inputStream;
        }

        @Override
        public String getCharacterEncoding() {
            return super.getCharacterEncoding() != null ? super.getCharacterEncoding() : WebUtils.DEFAULT_CHARACTER_ENCODING;
        }

        @Override
        public BufferedReader getReader() throws IOException {
            if (this.reader == null) {
                this.reader = new BufferedReader(new InputStreamReader(inputStream, getCharacterEncoding()));
            }
            return this.reader;
        }

        private class RequestCachingInputStream extends ServletInputStream {

            private final ServletInputStream is;

            private RequestCachingInputStream(ServletInputStream is) {
                this.is = is;
            }

            @Override
            public int read() throws IOException {
                int ch = is.read();
                if (ch != -1) {
                    bos.write(ch);
                }
                return ch;
            }

        }

    }
}

It was posted here on SO some time ago, but I can`t find original anwser to give credit to.

chimmi
  • 2,054
  • 16
  • 30