本文介绍了Spring返回HTTP 406的JSON(NOT_ACCEPTABLE)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Spring允许在 @RestControllerAdvice 内定义 @ExceptionHandlers



我已经在那里为HTTP 400、404、405等定义了许多其他 ExceptionHandlers ...但是HTTP 406的ExceptionHandler(NOT_ACCEPTABLE)确实可以似乎不起作用。



我的目标是返回带有JSON正文的HTTP 406。



p>

变体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)
返回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响应:

而不是

请求标头:




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


实际响应标题:




    内容类型:text / html


预期响应标头:




    Content-Type:application / json


我知道我可以简单地修复由客户端,但是如果服务器不知道如何响应,服务器应该始终以JSON响应。



我使用Spring 4.3.3.RELEASE和Jackson 2.8.4。 / p>

解决方案

最后,我找到了解决方案:



而不是返回

  private final ObjectMapper objectMapper = new ObjectMapper(); 

@ExceptionHandler(HttpMediaTypeNotAcceptableException.class)
public ResponseEntity< byte []> requestMethodNotSupported(HttpMediaTypeNotAcceptableException e){
对象响应= ...;
try {
return ResponseEntity.status(HttpStatus.NOT_ACCEPTABLE)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.body(objectMapper.writeValueAsBytes(response));
} catch(Exception subException){
//不应该发生!!!
subException.addSuppressed(e);
throw subException;
}
}

编辑:



作为替代方案,您可以为 ErrorResponse HttpMessageConverter< ErrorResponse> c $ c>对象。




    转到您的目录或创建一个 WebMvcConfigurerAdapter#extendMessageConverters(converters)

    code>
    选择一个能够创建预期结果/内容类型的 HttpMessageConverter
    将其包装为满足以下条件的方式:


      getSupportedMediaTypes()返回 MediaType.ALL
      canRead()返回false
      canWrite()仅对您的 ErrorResponse
      返回true write()设置强制CT并将您期望的内容类型转发给包装的转换器。

    将包装器添加到转换器列表。


      如果添加为第一个元素,它将始终返回您的预期结果(强制)


        请求:json,返回:强制CT
        请求:xml,返回:强制CT
        请求:image,返回:强制CT

      如果添加为最后一个元素,则如果没有其他匹配的转换器(备用)$ b,它将仅返回预期结果
      $ b

        请求:json,返回:json
        请求:xml,返回:xml
        请求:图像,返回:强制CT



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:

instead of

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.

解决方案

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() returns MediaType.ALLcanRead() returns false canWrite()returns only true for your ErrorResponsewrite() 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 CTRequested: xml , Returned: forced CTRequested: 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: jsonRequested: xml , Returned: xmlRequested: image , Returned: forced CT

这篇关于Spring返回HTTP 406的JSON(NOT_ACCEPTABLE)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-11 03:57