Reputation: 278
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);
}
}
Upvotes: 1
Views: 2685
Reputation: 21134
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.
Upvotes: 2