Spring为HTTP 406返回JSON(NOT_ACCEPTABLE)

时间:2016-11-04 11:06:47

标签: java spring rest http exception-handling

Spring允许在@ExceptionHandlers内定义@RestControllerAdvice

我已经在那里为HTTP 400,404,405定义了很多其他ExceptionHandlers ......但是HTTP 406的ExceptionHandler(NOT_ACCEPTABLE)似乎不起作用。处理程序被触发,我在日志中检查了它,但结果未被使用。

我的目标是返回带有JSON主体的HTTP 406。

变体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;
}

变体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);
}

但我总是从Tomcat获得与此类似的HTML响应:

  

HTTP状态406 -

     

类型:状态报告

     

消息:

     

description:标识的资源   此请求只能生成响应   根据要求不可接受的特征&#34;接受&#34;   头。

而不是

  

{   &#34; errorCode&#34;:406,   &#34; errorMessage&#34;:&#34; http.media_not_acceptable&#34;   }

请求报头:

  • 接受:申请/东西 - 不能存在

实际 - 响应 - 接头

  • 内容类型:text / html

预期 - 响应 - 接头

  • Content-Type:application / json

我知道我可以简单地修复&#34;客户端发送的Accept-Header,但是如果服务器不知道如何响应,则应始终以JSON响应。

我使用Spring 4.3.3.RELEASE和Jackson 2.8.4。

2 个答案:

答案 0 :(得分:6)

最后我找到了解决方案:

不是返回可序列化对象,而是直接返回字节。

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;
    }
}

修改

作为替代方案,您可以为HttpMessageConverter<ErrorResponse>对象创建自定义ErrorResponse

  • 转到您或创建WebMvcConfigurerAdapter#extendMessageConverters(converters)
  • 的impl
  • 选择一个能够创建预期结果/内容类型的HttpMessageConverter
  • 以满足以下条件的方式包裹它:
    • getSupportedMediaTypes()返回MediaType.ALL
    • canRead()返回false
    • canWrite()仅对您的ErrorResponse
    • 返回true
    • write()设置强制CT并将您预期的内容类型转发到包装转换器。
  • 将您的包装器添加到转换器列表中。
    • 如果作为第一个元素添加,那么它将始终返回您的预期结果(强制)
      • 请求:json,返回:强迫CT
      • 请求:xml,返回:强制CT
      • 要求:图片,退回:强制CT
    • 如果添加为最后一个元素,那么只会返回结果作为预期结果,如果没有其他匹配转换器(后备)
      • 请求:json,返回:json
      • 请求:xml,返回:xml
      • 要求:图片,退回:强制CT

答案 1 :(得分:1)

以@ST-DDT 调查结果为基础。如果您还扩展了 ResponseEntityExceptionHandler,那么您不能只添加另一个方法来处理 HttpMediaTypeNotAcceptableException。但是,对于整个问题,还有一个更简单的解决方案:

    @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());
    }