How can one customize the message
field in the error response body (and at the same time customize the http status code)?
Based on the answer to #1677 I attempted to use the sendError(int, String) method to customize both the http status code and the message
field simultaneously:
@ExceptionHandler(IllegalArgumentException.class)
void handleIllegalArgumentException(HttpServletResponse response) throws IOException {
response.sendError(HttpStatus.BAD_REQUEST.value(), "custom message");
}
When a request causes an IllegalArgumentException
the client will get the following response:
HTTP Status 400 - Bad Request
{
"timestamp": 1413702719532,
"status": 400,
"error": "Bad Request",
"exception": "java.lang.IllegalArgumentException",
"message": "original exception message",
"path": "/test"
}
The problem is that I expected the message
field to equal custom message
(or whatever the value was passed to message parameter to the sendError(int, java.lang.String)
method). Instead, I see original exception message
(i.e. the value that was passed to the IllegalArgumentException
constructor).
Side note, the http status header and all other fields in the response body are correct.
Related to #1677 Spring Boot version 1.1.8
Comment From: wilkinsona
You can hide the exception from DefaultErrorAttributes
by clearing a request attribute:
@ExceptionHandler(IllegalArgumentException.class)
void handleIllegalArgumentException(HttpServletRequest request, HttpServletResponse response) throws IOException {
request.setAttribute(DefaultErrorAttributes.class.getName() + ".ERROR", null);
response.sendError(HttpStatus.BAD_REQUEST.value(), "custom message");
}
If you do this you'll also lose the exception
entry in the response. You should consider this a workaround at the moment. The attribute name is really an implementation detail that might change. We can use this issue to explore the possibility of doing something more official.
Comment From: matsev
Ok. I am also considering another workaround for now, i.e. wrapping the method call in try block and throw a new exception with the desired message in the catch block. It is not pretty, especially not if the same exception needs to be caught in several places.
Comment From: wilkinsona
I neglected to mention earlier that you can provide your own ErrorAttributes
bean and have complete control over the contents of the JSON. You could subclass DefaultErrorAttributes
to make the custom message set via sendError
take precedence:
@Bean
public ErrorAttributes errorAttributes() {
return new DefaultErrorAttributes() {
@Override
public Map<String, Object> getErrorAttributes(
RequestAttributes requestAttributes,
boolean includeStackTrace) {
Map<String, Object> errorAttributes = super.getErrorAttributes(requestAttributes, includeStackTrace);
Object errorMessage = requestAttributes.getAttribute(RequestDispatcher.ERROR_MESSAGE, RequestAttributes.SCOPE_REQUEST);
if (errorMessage != null) {
errorAttributes.put("message", errorMessage);
}
return errorAttributes;
}
};
}
Comment From: dsyer
With that change in #1762 sendError(int,String)
should work as expected. I believe the same thing should apply with @ResponseStatus
on a method or an Exception
(which is better than having to inject HttpServletResponse
). The method has to be a void return type I think.
Comment From: singhalavi
i spent couple of hours sorting it out, thought of sharing it for others benefit.
you can do it this way
@ExceptionHandler(ServiceException.class) @ResponseBody public ErrorResponse handleServiceException(HttpServletRequest req, HttpServletResponse response, ServiceException e) { ErrorResponse error = new ErrorResponse(); error.setResponseMessage(e.getMessage()); //Set custom non standard http status code response.setStatus(499); return error; }
ErrorResponse is a normal java object
Comment From: Kartikkman
i spent couple of hours sorting it out, thought of sharing it for others benefit.
you can do it this way
@ExceptionHandler(ServiceException.class) @ResponseBody public ErrorResponse handleServiceException(HttpServletRequest req, HttpServletResponse response, ServiceException e) { ErrorResponse error = new ErrorResponse(); error.setResponseMessage(e.getMessage()); //Set custom non standard http status code response.setStatus(499); return error; }
ErrorResponse is a normal java object
Hi @singhalavi , It worked Thanks a lot man !! Could you please also share the reason behind it working !!
Comment From: RajHirani
You can also use @ControllerAdvice @matsev
@ControllerAdvice
@Slf4j
public class ExceptionHelper {
@ExceptionHandler(value = { UserAlreadyExistAuthenticationException.class })
public ResponseEntity handleInvalidInputException(UserAlreadyExistAuthenticationException ex) {
log.error("UserAlreadyExistAuthenticationException: ",ex.getMessage());
return new ResponseEntity<Object>(ex.getMessage(), HttpStatus.BAD_REQUEST);
}
}