0

I have a @RestControllerAdvice where I handle an Exception in Spring Boot. I would like to log an information that is sent through request body. How can I get this information from a spring WebRequest?

This is my sample exception handler.

@RestControllerAdvice
public class CustomExceptionHandler extends ResponseEntityExceptionHandler {

@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
        HttpHeaders headers, HttpStatus status, WebRequest request) {

    // I want to add something here that I could log an info that is in the request body.
    return super.handleMethodArgumentNotValid(ex, headers, status, request);
}

}

@M.Deinum I tried to use ContentCachingRequestWrapper, But I could not have acess to body content. The method contentCachingRequestWrapper.getContentAsByteArray() returns null.

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
      throws IOException, ServletException {

    try {
        ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper((HttpServletRequest) request);
        wrappedRequest.getContentAsByteArray();
        wrappedRequest.getInputStream();
        chain.doFilter(wrappedRequest, response);
    } finally {
        LoggingContext.clear();
    }
  • Maybe this could help https://stackoverflow.com/questions/21193380/get-requestbody-and-responsebody-at-handlerinterceptor – Aman Singh Rajpoot Apr 13 '22 at 09:49
  • This link can help you out with this I guess https://stackoverflow.com/a/46046430/16156784 –  Apr 13 '22 at 10:03
  • A request body can only be consumed once, if you want to read it multiple times, you need to think about that. You need a filter that will map the request with a request that allows the body to be read multiple times (like the `ContentCachingRequestWrapper` in Spring). If you have that, you can just add the `HttpServletRequest` and read the body again. Ifyou don't, you won't have a way of reading it again. – M. Deinum Apr 13 '22 at 11:37
  • @M.Deinum thank you. I tried to use ContentCachingRequestWrapper, But I could not have acess to body content. The method contentCachingRequestWrapper.getContentAsByteArray() returns null. – Diego Victor Apr 13 '22 at 13:57
  • Ofcourse it returns `null` at that position as nothing has been read yet. It will only have content **after** things have been read through the inputstream. – M. Deinum Apr 13 '22 at 14:15
  • @M.Deinum how can I guarantee that things have been read in that part in order to log body data? – Diego Victor Apr 13 '22 at 14:28
  • You just need to get the inputstream and read it. Also where did I say you need to write/log it in the filter. You need to wrap it so you can read it again in your exception handler, that is what you were trying to achieve. – M. Deinum Apr 13 '22 at 14:40
  • Yeah. I will get the data I want and store them in a LogginContext. In the exception handler, I will get the values in LogginContext. So, to get the body data, I just need to do: httpRequest.getInputStream() ? – Diego Victor Apr 13 '22 at 15:10

2 Answers2

0

RequestBody and ResponseBody can be read-only once so other ways to achieve the body in the exception handler Visit here

Imagica
  • 11
  • 2
  • That doesn't get you the body, that gets you the resulting object, which in case of an error might not even exist... So that solution won't work. – M. Deinum Apr 13 '22 at 11:36
  • That's true. In case of a MethodArgumentNotValidException, the request body object would not be constructed. In this scenario, you will not be able to have the object annotated with @RequestBody – Diego Victor Apr 13 '22 at 13:13
0

The comments regarding using the ContentCachingRequestWrapper are accurate, this is the implementation using your controller advice that should work.

@Component
public class MyFilter implements Filter {
  @Override
  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
  throws IOException, ServletException {
     ContentCachingRequestWrapper contentCachingRequestWrapper = new ContentCachingRequestWrapper(
    (HttpServletRequest) servletRequest);

     filterChain.doFilter(contentCachingRequestWrapper, servletResponse);
  }
}

The advice

@RestControllerAdvice
public class CustomExceptionHandler extends ResponseEntityExceptionHandler {

  @Override
  protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers,
  HttpStatus status, WebRequest request) {

    ContentCachingRequestWrapper nativeRequest = (ContentCachingRequestWrapper) ((ServletWebRequest) request).getNativeRequest();
    String requestEntityAsString = new String(nativeRequest.getContentAsByteArray());

    log.debug(requestEntityAsString);

    return super.handleMethodArgumentNotValid(ex, headers, status, request);
  }
}
lane.maxwell
  • 3,132
  • 1
  • 18
  • 22