Spring allows the definition of @ExceptionHandlers
inside of @RestControllerAdvice
.
I already defined a lot of other ExceptionHandlers
in there for HTTP 400, 404, 405,... However the ExceptionHandler for HTTP 406 (NOT_ACCEPTABLE) does not seem to work. The handler is triggered, I checked that in the logs, but the result is not used.
My goal is it to return a HTTP 406 with a JSON body.
Variant 1
@ResponseStatus(HttpStatus.NOT_ACCEPTABLE)
@ExceptionHandler(HttpMediaTypeNotAcceptableException.class)
public ErrorDTO requestMethodNotSupported(final HttpMediaTypeNotAcceptableException e) {
final ErrorDTO dto = new ErrorDTO(HttpStatus.NOT_ACCEPTABLE, "http.media_not_acceptable");
return dto;
}
Variant 2
@ExceptionHandler(HttpMediaTypeNotAcceptableException.class)
public ResponseEntity<ErrorDTO> requestMethodNotSupported2(final HttpMediaTypeNotAcceptableException e) {
final ErrorDTO dto = new ErrorDTO(HttpStatus.NOT_ACCEPTABLE, "http.media_not_acceptable");
return ResponseEntity.status(HttpStatus.NOT_ACCEPTABLE).contentType(MediaType.APPLICATION_JSON_UTF8).body(dto);
}
But I always get a HTML response similar to this from the Tomcat:
HTTP Status 406 -
type: Status report
message:
description: The resource identified by this request is only capable of generating responses with characteristics not acceptable according to the request "accept" headers.
instead of
{ "errorCode": 406, "errorMessage": "http.media_not_acceptable" }
Request-Headers:
Actual-Response-Headers:
Expected-Response-Headers:
I know that I could simply "fix" the Accept-Header that is send by the client, however the server should always respond in JSON, if it does not know how to respond.
I use Spring 4.3.3.RELEASE and Jackson 2.8.4.
Finally I found a solution for this:
Instead of returning a serializable object just return the bytes directly.
private final ObjectMapper objectMapper = new ObjectMapper();
@ExceptionHandler(HttpMediaTypeNotAcceptableException.class)
public ResponseEntity<byte[]> mediaTypeNotAcceptable(HttpMediaTypeNotAcceptableException e) {
Object response = ...;
try {
return ResponseEntity.status(HttpStatus.NOT_ACCEPTABLE)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.body(objectMapper.writeValueAsBytes(response));
} catch (Exception subException) {
// Should never happen!!!
subException.addSuppressed(e);
throw subException;
}
}
EDIT:
As an alternative you can create a custom HttpMessageConverter<ErrorResponse>
for your ErrorResponse
object.
WebMvcConfigurerAdapter#extendMessageConverters(converters)
HttpMessageConverter
that is capable of creating your expected result/content type.getSupportedMediaTypes()
returns MediaType.ALL
canRead()
returns falsecanWrite()
returns only true for your ErrorResponse
write()
sets the forced CT and forward your expected content type to the wrapped converter.Building on @ST-DDT findings. If you are also extending ResponseEntityExceptionHandler
then you cannot just add another method to handle HttpMediaTypeNotAcceptableException
. However, there is an even simpler solution to the whole problem then:
@Override
public ResponseEntity<Object> handleHttpMediaTypeNotAcceptable(HttpMediaTypeNotAcceptableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
ResponseEntity<Object> response = super.handleHttpMediaTypeNotAcceptable(ex, headers, status, request);
// Workaround to return JSON response for 406
return ResponseEntity.status(NOT_ACCEPTABLE)
.contentType(APPLICATION_JSON)
.body(response.getBody());
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With