问题
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:
- Accept: application/stuff-that-cannot-be-present
Actual-Response-Headers:
- Content-Type: text/html
Expected-Response-Headers:
- Content-Type: application/json
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.
回答1:
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[]> requestMethodNotSupported(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.
- Go to your or create a impl of
WebMvcConfigurerAdapter#extendMessageConverters(converters)
- Pick a
HttpMessageConverter
that is capable of creating your expected result/content type. - Wrap it in a way to fulfill the following conditions:
getSupportedMediaTypes()
returnsMediaType.ALL
canRead()
returns falsecanWrite()
returns only true for yourErrorResponse
write()
sets the forced CT and forward your expected content type to the wrapped converter.
- Add your wrapper to the converters list.
- If added as first element then it will always return your expected result (forced)
- Requested: json , Returned: forced CT
- Requested: xml , Returned: forced CT
- Requested: image , Returned: forced CT
- If added as last element then it will only return the result as your expected result, if there was no other matching converter (fallback)
- Requested: json , Returned: json
- Requested: xml , Returned: xml
- Requested: image , Returned: forced CT
- If added as first element then it will always return your expected result (forced)
来源:https://stackoverflow.com/questions/40421119/spring-return-json-for-http-406-not-acceptable