繁体   English   中英

Spring 为 HTTP 406 返回 JSON (NOT_ACCEPTABLE)

[英]Spring return JSON for HTTP 406 (NOT_ACCEPTABLE)

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

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

我的目标是返回一个带有 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 -

类型:状态报告

消息:

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

而不是

{ "errorCode": 406, "errorMessage": "http.media_not_acceptable" }

请求头:

  • 接受:应用程序/不能存在的东西

实际响应头:

  • 内容类型:文本/html

预期响应头:

  • 内容类型:应用程序/json

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

我使用 Spring 4.3.3.RELEASE 和 Jackson 2.8.4。

最后我找到了一个解决方案:

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

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

编辑:

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM