3

When a client request for a resource producing application/json content with Accept Header of application/xml. The request fails with HttpMediaTypeNotAcceptableException exception and is wrapped into error message body in the response entity object by using exception handler annotation as mentioned in below code. However, we receive HttpMediaTypeNotAcceptableException again when return values are written to the response with HttpMessageConverter. It is because it checks the producible content type for the response with the acceptable request type, but this is exactly something we are trying to communicate to the client using error message. How do I workaround this issue ? Btw, all the other exceptions are parsing fine to error message. Please advise.

@ControllerAdvice
    public class RestExceptionHandler extends ResponseEntityExceptionHandler {

     @Override
      protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body,
          HttpHeaders headers, HttpStatus status, WebRequest request) {
          // Setting the response content type to json
          headers.setContentType(MediaType.APPLICATION_JSON);
        return ResponseEntity.status(status).headers(headers).body(body);
      }
    }
s7vr
  • 73,656
  • 11
  • 106
  • 127

3 Answers3

0

A few options come to my mind. One is that your controller method produces all content types and then you throw an exception in your method if the content type is not the one you are expecting, then the exception handler can take this exception and transform it. This is the only one that works with exception handlers, as exception handlers only deal with exceptions produced in the controller method.

The other options are:

  • Use an interceptor (but I'm not sure if this will work, as Spring might try to resolve first the controller method rather than invoking the interceptors).
  • Extend RequestMappingHandlerMapping to call the exception handler if it doesn't find a suitable method. You'll probably need to override the method handleNoMatch. In there you'll need to get a reference to the list of HandlerExceptionResolver

The first one is the simplest to understand, and the latest one might be the most 'extensible', but it also requires some understanding of the internals of Spring.

Augusto
  • 28,839
  • 5
  • 58
  • 88
  • 1
    Thank you for the quick response.If you haven't notice, I'm extending the ResponseEntityExceptionHandler, so that I can add the error message to the body of the response and I have override the protected method, so all the exceptions can be written as error message. Spring message converter then resolves content type to application/json;charset=UTF-8 or application/*+json;charset=UTF-8 based on return value( my error message object in this case) and compares with the accept header type (application/xml) and throws the HttpMediaTypeNotAcceptableException again when writing to the response. – s7vr Aug 07 '15 at 20:51
  • Yes, actually issue is even though our ExceptionHandler gets called and we return our custom error object, spring again tries to find response type based on the producible and acceptable types (inside AbstractMessageConverterMethodProcessor). And if mime type requested is producible (i.e HttpMessageConveter) is available for that type, then your custom error object returned from ExceptionHandler will get written to response as per Accept header, otherwise, spring will fallback to HTML – Amit Patil Nov 26 '19 at 17:02
0

Resolved by setting different content negotiation strategy FixedContentNegotiationStrategy for ExceptionHandlerExceptionResolver and HeaderContentNegotiationStrategy for RequestResponseBodyMethodProcessor.

s7vr
  • 73,656
  • 11
  • 106
  • 127
0

I have been using a serialized enum-based response (enum annotated with jackson @JsonFormat(shape = Shape.OBJECT) to standardize the error messages in my exception handler class and faced the same issue when it caught with a HttpMediaTypeNotAcceptableException.

The workaround is to set the media type you expect to return directly to the builder method available in the ResponseEntity.

The below code works fine for me.

@ExceptionHandler(HttpMediaTypeNotAcceptableException.class)
public final ResponseEntity<ResponseMessagesEnum> handleHttpMediaTypeNotAcceptableException(
        HttpMediaTypeNotAcceptableException e, HttpServletRequest request) {
    logger.error("No acceptable representation found for [{}] | supported {}", request.getHeader("Accept"), e.getSupportedMediaTypes());
    return ResponseEntity.status(HttpStatus.BAD_REQUEST).contentType(MediaType.APPLICATION_JSON)
            .body(ResponseMessagesEnum.EX_001);
}