Spring返回HTTP 406的JSON(NOT_ACCEPTABLE)

ST-*_*DDT 2 java rest spring exception-handling http

Spring允许在@ExceptionHandlers内部定义@RestControllerAdvice

我已经ExceptionHandlers在那里为HTTP 400、404、405 等定义了许多其他协议。但是,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;
}
Run Code Online (Sandbox Code Playgroud)

变体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);
}
Run Code Online (Sandbox Code Playgroud)

但是我总是从Tomcat收到类似于以下内容的HTML响应:

HTTP状态406-

类型:状态报告

信息:

描述:此请求标识的资源只能根据请求“接受”标头生成特性不可接受的响应。

代替

{“ errorCode”:406,“ errorMessage”:“ http.media_not_acceptable”}

请求标头:

  • 接受:应用程序/无法显示的东西

实际响应标题:

  • 内容类型:text / html

预期响应标题:

  • 内容类型:application / json

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

我使用Spring 4.3.3.RELEASE和Jackson 2.8.4。

ST-*_*DDT 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:

或者,您可以HttpMessageConverter<ErrorResponse>ErrorResponse对象创建自定义。

  • 前往或建立 WebMvcConfigurerAdapter#extendMessageConverters(converters)
  • 选择一个HttpMessageConverter能够创建预期结果/内容类型的。
  • 将其包装以符合以下条件:
    • getSupportedMediaTypes() 退货 MediaType.ALL
    • canRead() 返回假
    • canWrite()只为您返回true ErrorResponse
    • write() 设置强制CT并将您期望的内容类型转发给包装的转换器。
  • 将包装器添加到转换器列表。
    • 如果添加为第一个元素,则它将始终返回您的预期结果(强制)
      • 要求:json,返回:强制CT
      • 要求:xml,返回:强制CT
      • 要求:图片,返回:强制CT
    • 如果添加为最后一个元素,那么如果没有其他匹配的转换器,它只会返回预期结果(回退)
      • 要求:json,返回:json
      • 要求:xml,返回:xml
      • 要求:图片,返回:强制CT