3

我有一个基于Spring MVC的 Rest web 服务。我使用 a@RestControllerAdvice来处理从我的 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_VALUEcustomException


异常处理程序

my 的定义@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);
}

根据请求accept header,如果没有设置accept,则@ExceptionHandler返回null body和NOT_ACCEPTABLEhttp响应状态,ErrorDto如果accept是typeAPPLICATION_JSON或者JaxbElementofErrorDto是accept是type ,则返回type对象APPLICATION_XML

请注意,当响应包含正文时,我指定了响应的内容类型。


问题

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

当客户端调用一个引发异常(然后返回 errorDto)的方法时,具有多个 Accept 标头,如下所示:

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
4

1 回答 1

0

春天有这个错误。根据RFC2616,406 不可接受

  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 服务的错​​误处理是最好强制响应简单的东西(例如 text/plain)以向客户端获取有意义的错误消息的情况之一。

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

于 2018-09-14T16:47:06.353 回答