When a http request causes a web application to throw an exception such as new IllegalArgumentException("Bad argument") the client receives a response with a default error message:
HTTP Status 500 - Internal Server Error
{
"timestamp": 1412861896541,
"status": 500,
"error": "Internal Server Error",
"exception": "java.lang.IllegalArgumentException",
"message": "Bad argument",
"path": "/test"
}
The http response status code can be changed by using a @ControllerAdvice such as:
@ControllerAdvice
class CustomControllerAdvice extends DefaultHandlerExceptionResolver {
@ExceptionHandler
@ResponseStatus(HttpStatus.BAD_REQUEST)
void handleIllegalArgumentException(IllegalArgumentException e) {
}
}
By doing so the status code is indeed updated, but the response body is not present anymore. Preferably, the client should receive a similar response as before, but with the new status code.
HTTP Status 400 - Bad Request
{
"timestamp": 1412861896541,
"status": 400,
"error": "Bad Request",
"exception": "java.lang.IllegalArgumentException",
"message": "Bad argument",
"path": "/test"
}
Are you using an embedded servlet container or deploying to a standalone container?
Don't you need to actually handle the response as well (i.e. add a body)? You can do that with @ResponseBody. If tyou want the default response body you can get it from an @Autowired ErrorAttributes.
@wilkinsona I am using an embedded Tomcat for development, but I will use a standalone Tomcat in production.
@dsyer I autowired the ErrorAttributes as you suggested and did the following change:
@ExceptionHandler
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
Map<String, Object> handleIllegalArgumentException(IllegalArgumentException e, WebRequest request) {
return errorAttributes.getErrorAttributes(request, false);
}
Which resulted in
HTTP Status 400 - Bad Request
{
"timestamp": 1412921853925,
"status": 999,
"error": "None",
"exception": "java.lang.IllegalArgumentException",
"message": "Bad argument"
}
i.e. the HTTP status is correct but path is missing in the response body and status and error have not been set correctly. The same result is observed if a ResponsesEntity is used instead, .e.g
@ExceptionHandler
ResponseEntity<Map<String, Object>> handleIllegalArgumentException(IllegalArgumentException e, WebRequest request) {
Map<String, Object> body = errorAttributes.getErrorAttributes(request, false);
return new ResponseEntity<>(body, HttpStatus.BAD_REQUEST);
}
On the other hand, if the exception declaration is moved from the method signature to the @ExceptionHandler, e.g.
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(IllegalAccessException.class)
@ResponseBody
Map<String, Object> handleIllegalArgumentException(WebRequest request) {
return errorAttributes.getErrorAttributes(request, false);
}
Then the response status is not honored, the status code is 500, and the status and error in the response body reflects the 500 and not the expected 400 (again, the same result is observed when returning a ResponseEntity):
HTTP Status 500 - Internal Server Error
{
"timestamp": 1412922950364,
"status": 500,
"error": "Internal Server Error",
"exception": "java.lang.IllegalArgumentException",
"message": "Bad argument",
"path": "/test"
}
This is probably the easiest way to get the response that you want:
@ExceptionHandler
void handleIllegalArgumentException(HttpServletResponse response) throws IOException {
response.sendError(HttpStatus.BAD_REQUEST.value());
}
The path is omitted when you try to use ErrorAttributes yourself as it uses the javax.servlet.error.request_uri request attribute which isn't set until the request's been forwarded for error handling.
Nice!
I have verified that it works both when adding the exception as a method parameter:
@ExceptionHandler
void handleIllegalArgumentException(HttpServletResponse response, IllegalArgumentException e) throws IOException {
response.sendError(HttpStatus.BAD_REQUEST.value());
}
as well as when providing it as an annotation value:
@ExceptionHandler(IllegalArgumentException.class)
void handleIllegalArgumentException(HttpServletResponse response) throws IOException {
response.sendError(HttpStatus.BAD_REQUEST.value());
}
Both solutions result in the same response:
HTTP Status 400 - Bad Request
{
"timestamp": 1413283491004,
"status": 400,
"error": "Bad Request",
"exception": "java.lang.IllegalArgumentException",
"message": "Bad argument",
"path": "/test"
}
Thanks!
Excellent. Thanks for following up.
@ExceptionHandler(CustomException.class)
void handleCustomExcepption(HttpServletResponse response) throws IOException {
response.sendError(HttpStatus.NO_CONTENT.value());
}
won't produce default error message as of Spring Boot 1.5.5
@ZaitsevY This issue has been closed for almost two years. If you believe you have found a problem please open a new issue with a small sample that reproduces it.
See #5774 for a more recent discussion.
Based on a hint in one of the comments there, the following code seems to successfully modify the HTTP status code without losing the response body (using Spring Boot 1.5.6):
@ControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(MyException.class)
public void handle(HttpServletResponse response) throws IOException {
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
}
}
Most helpful comment
This is probably the easiest way to get the response that you want:
The path is omitted when you try to use
ErrorAttributesyourself as it uses thejavax.servlet.error.request_urirequest attribute which isn't set until the request's been forwarded for error handling.