java - Spring HttpMessageConverter 根据@ExceptionHandler响应内容类型

标签 java spring spring-mvc spring-boot exceptionhandler

我有一个基于 Spring MVC 的 Rest Web 服务。我用@RestControllerAdvice处理从我的 @Controller 抛出的异常s。

<小时/>

Controller

调用示例如下

@GetMapping(value = "/{id}/{name:.+}", produces = { MediaType.APPLICATION_OCTET_STREAM_VALUE,
        MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<byte[]> getSomething(
        @PathVariable("id") String id, @PathVariable("name") String name) throws customException;

可以生成 3 种媒体类型的基本方法:APPLICATION_OCTET_STREAM_VALUE , APPLICATION_XML_VALUEAPPLICATION_JSON_VALUE并抛出 customException

<小时/>

异常处理程序

我的@RestControllerAdvice的定义如下:

@ExceptionHandler({ CustomException.class })
public ResponseEntity<Object> handleException(CustomException e) {

    ErrorDto err = errorMapper.map(e);
    Enumeration<String> en = httpServletRequest.getHeaders(HttpHeaders.ACCEPT);

    while (en.hasMoreElements()) {
        String list = en.nextElement();
        StringTokenizer st = new StringTokenizer(list, ",");

        while (st.hasMoreTokens()) {

            String acc = st.nextToken();
            MediaType contentTypeHeader = MediaType.valueOf(acc);

            if (MediaType.APPLICATION_XML.includes(contentTypeHeader)) {

                JAXBElement<ErrorDto> ret = new ObjectFactory().createError(err);
                return ResponseEntity.status(HttpStatus.PRECONDITION_FAILED)
                        .contentType(MediaType.APPLICATION_XML).body(ret);

            } else if (MediaType.APPLICATION_JSON.includes(contentTypeHeader)) {

                return ResponseEntity.status(HttpStatus.PRECONDITION_FAILED)
                        .contentType(MediaType.APPLICATION_JSON).body(err);

            }
        }
    }

    return ResponseEntity.status(HttpStatus.NOT_ACCEPTABLE).body(null);
}

根据请求接受 header ,@ExceptionHandler返回一个 null 主体和 NOT_ACCEPTABLE如果未设置接受或类型为 ErrorDto 的对象,则 http 响应状态如果接受的类型为APPLICATION_JSONJaxbElementErrorDto接受的类型为 APPLICATION_XML

Please note that I specify the content type of the response when it contains a body.

<小时/>

问题

我的问题是,当客户端使用多个接受 header 进行调用时,Spring 会尝试获取 HttpMessageConverter根据接受 header 而不是响应内容类型。下面是一个例子:

当客户端调用抛出异常(然后返回 errorDto)且具有多个 Accept header 的方法时,如下所示:

HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.ACCEPT, MediaType.APPLICATION_OCTET_STREAM_VALUE);
headers.add(HttpHeaders.ACCEPT, MediaType.APPLICATION_XML_VALUE);
HttpEntity<?> entity = new HttpEntity<>(body, headers);

restTemplate.exchange(uriComponents.encode().toUri(), httpMethod, entity, Class);

Spring 没有按预期返回 XML 格式的错误响应。它寻找 Octet-Stream <-> JaxbElement不存在的转换器,不会尝试根据响应内容类型查找转换器。

如何强制 Spring 根据响应内容类型使用转换器?

<小时/>

我正在使用:

  • Spring Boot 1.4.0.RELEASE

最佳答案

Spring 有这个错误。根据RFC2616, 406 Not Acceptable :

  Note: HTTP/1.1 servers are allowed to return responses which are
  not acceptable according to the accept headers sent in the
  request. In some cases, this may even be preferable to sending a
  406 response. User agents are encouraged to inspect the headers of
  an incoming response to determine if it is acceptable.

根据我的经验,REST 服务的错​​误处理是最好强制响应简单内容(例如文本/纯文本)的情况之一,以便向客户端获取有意义的错误消息。

Spring 这里的过度固执行为具有掩盖 406 响应中发生的主要错误的效果。因此,上述 RFC 注释同样适用于所有 4xx 和 5xx 响应。如果您无法匹配已接受的内容类型,那么您可能不应该发送 1xx、2xx 或 3xx 范围内的任何响应。

关于java - Spring HttpMessageConverter 根据@ExceptionHandler响应内容类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41827742/

相关文章:

java - Spring MVC 3.1 根据请求语言环境更改语言 i18n

java.lang.NoSuchMethodError : org. springframework.core.annotation.AnnotatedElementUtils.findMergedAnnotation

java - 如何通过输入将尽可能多的元素添加到数组列表中?

java - 我们给 spring 注解起个名字是有原因的吗?

java - 更改 Spring 应用程序启动消息

java - 如何设计一个 View 来模拟 Web 服务调用时的自动转换?

java - Java服务使用什么框架+应用服务器+开发环境?

java - 为什么此类的compareTo 方法没有将边<A,B> 和<B,A> 返回为同一事物?

java - 将 dateTime 转换为 int

java - 在fragment中findViewById,如何在onCreateView之外获取 View