I have my own Exceptionhandler which is extending ResponseEntityExceptionHandler
I am able to capture the error but the request body is empty at the time of error response creation
override fun handleHttpMessageNotReadable(e:HttpMessageNotReadableException, headers:HttpHeaders , status:HttpStatus , webRequest: WebRequest):ResponseEntity<Any>{ val rsp = ErrResponse( Data( HttpStatus.BAD_REQUEST.name, e.message!! ),**REQUEST-BODY-NEEDED**[customFilter.payload]) return super.handleExceptionInternal(e, rsp,headers, HttpStatus.BAD_REQUEST, webRequest) } So i have used customRequestfilter to get the body and captured the body there but the order precendence is low for customRequestFilter it will be get executed only after the request . So is there a any way to Capture the request body on the error response?
CustomRequestFilter
@Component public class CustomRequestFilter extends OncePerRequestFilter{ public String payload; public Map<String, Object> reqLog =null; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request); int status = HttpStatus.BAD_REQUEST.value(); filterChain.doFilter(wrappedRequest, response); if (status == response.getStatus()) { reqLog = getTrace(wrappedRequest, status); payload = getBody(wrappedRequest, reqLog);/** ITS CAPTURING THE BODY HERE SUCCESSFULLY**/ logTrace(wrappedRequest, reqLog); } } 51 Answer
Wow, that was tricky! Anyway...
Create your custom HttpInputMessage, which will delegate to the original one.
class CachedHttpInputMessage implements HttpInputMessage { private final HttpInputMessage httpInputMessage; private ByteArrayOutputStream outputStream; CachedHttpInputMessage(final HttpInputMessage httpInputMessage) { this.httpInputMessage = httpInputMessage; } @Override public InputStream getBody() throws IOException { if (outputStream == null) { outputStream = new ByteArrayOutputStream(); final InputStream body = httpInputMessage.getBody(); final byte[] buffer = new byte[1024]; while (true) { final int length; if (!((length = body.read(buffer)) > -1)) { break; } outputStream.write(buffer, 0, length); } outputStream.flush(); } return new ByteArrayInputStream(outputStream.toByteArray()); } @Override public HttpHeaders getHeaders() { return httpInputMessage.getHeaders(); } } Build your custom HttpMessageConverter, extending the right one based on the currently used one (Jackson, Gson, etc.), and register it as first.
class CustomHttpMessageConverter extends MappingJackson2HttpMessageConverter { @Override public Object read( final Type type, final Class<?> contextClass, final HttpInputMessage inputMessage) throws IOException { return super.read(type, contextClass, new CachedHttpInputMessage(inputMessage)); } @Override protected Object readInternal( final Class<?> clazz, final HttpInputMessage inputMessage) throws IOException { return super.readInternal(clazz, new CachedHttpInputMessage(inputMessage)); } } (alternatively you could create a generic wrapper, like with the CachedHttpInputMessage, and wrap each pre-configured HttpMessageConverter, just update the list passed as input to extendMessageConverters)
@Configuration class WebConfiguration implements WebMvcConfigurer { @Override public void extendMessageConverters(final List<HttpMessageConverter<?>> converters) { converters.add(0, new CustomHttpMessageConverter()); } ... } Throw away the custom Filter, and inside the ExceptionHandler read the body using
final HttpInputMessage inputMessage = e.getHttpInputMessage(); final InputStream body = inputMessage.getBody(); Done!
Remember to clean-up the code a bit and handle all the possible exceptions.