处理状态为 NO_CONTENT 的响应时的 Spring RestTemplate 行为

好的,我有一个类NameEdSystems,它的唯一字段是NameEdSystem的集合。

我有一种方法可以根据某些标准找到命名系统。这并不重要。当它得到结果时,一切正常。但是,当它找不到任何东西,因此返回一个空(或空 - 我已经尝试过两种方式)集时,我遇到了问题。让我解释一下。

我正在使用Spring RestTemplate类,并在单元测试中进行这样的调用:

ResponseEntity<?> responseEntity = template.exchange(BASE_SERVICE_URL + "?
  alias={aliasValue}&aliasAuthority={aliasAssigningAuthority}", 
  HttpMethod.GET, makeHttpEntity("xml"), NamedSystems.class, 
  alias1.getAlias(), alias1.getAuthority());

现在,由于这通常会返回 200,但我想返回 204,因此我的服务中有一个侦听器,用于确定 ModelAndView 是否为 NamedSystem,以及其集合是否为 null。如果是这样,我将状态代码设置为NO_CONTENT(204)。

当我运行我的 junit 测试时,我收到此错误:

org.springframework.web.client.RestClientException: Cannot extract response: no Content-Type found

将状态设置为NO_CONTENT似乎会擦除内容类型字段(当我想到它时,这确实有意义)。那么,为什么它甚至会看着它呢?

Spring的HttpMessageConverterExtractor extractData方法:

public T extractData(ClientHttpResponse response) throws IOException {
    MediaType contentType = response.getHeaders().getContentType();
    if (contentType == null) {
        throw new RestClientException("Cannot extract response: no Content-Type found");
    }
    for (HttpMessageConverter messageConverter : messageConverters) {
        if (messageConverter.canRead(responseType, contentType)) {
            if (logger.isDebugEnabled()) {
                logger.debug("Reading [" + responseType.getName() + "] as \"" + contentType
                    +"\" using [" + messageConverter + "]");
            }
            return (T) messageConverter.read(this.responseType, response);
        }
    }
    throw new RestClientException(
        "Could not extract response: no suitable HttpMessageConverter found for response type [" +
        this.responseType.getName() + "] and content type [" + contentType + "]");
}

沿着链向上走一点,找出提取器的设置位置,我来到了我在测试中使用的RestTemplate的exchange()方法:

public <T> ResponseEntity<T> exchange(String url, HttpMethod method,
  HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables) throws RestClientException {
    HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(requestEntity, responseType);
    ResponseEntityResponseExtractor<T> responseExtractor = new ResponseEntityResponseExtractor<T>(responseType);
    return execute(url, method, requestCallback, responseExtractor, uriVariables);
}

因此,它正在尝试将相当于无的内容转换为无,因为从交换调用中提供了响应类型。如果我将 responseType 从 NamedSystems.class 更改为 null,它将按预期工作。它不会尝试转换任何内容。如果我尝试将状态代码设置为404,它也执行正常。

我是被误导了,还是这似乎是RestTemplate中的一个缺陷?当然,我现在正在使用junit,所以我知道会发生什么,但是如果有人使用RestTemplate来调用它并且不知道服务调用的结果,他们自然会将NamedSystems作为响应类型。但是,如果他们尝试了没有元素的标准搜索,他们就会遇到这个令人讨厌的错误。

有没有办法在不覆盖任何RestTemplate内容的情况下解决这个问题?我是否错误地查看了这种情况?请帮忙,因为我有点困惑。


答案 1

解决此问题的另一种方法是将响应实体设置为 null,如下所示。

  ResponseEntity<?> response = restTemplate.exchange("http://localhost:8080/myapp/user/{userID}",
                                                             HttpMethod.DELETE, 
                                                             requestEntity,
                                                             null,
                                                             userID);

如果仍然需要响应标头,请尝试实现响应错误处理程序。


答案 2

我相信你应该看看ResertExtractor接口和调用执行在RestTemplate上提供你的提取器实现。对我来说,这似乎是一个常见的要求,所以已经记录了这个:

https://jira.springsource.org/browse/SPR-8016

这是我之前准备的:

private class MyResponseExtractor extends HttpMessageConverterExtractor<MyEntity> {

    public MyResponseExtractor (Class<MyEntity> responseType,
      List<HttpMessageConverter<?>> messageConverters) {
        super(responseType, messageConverters);
    }

    @Override
    public MyEntity extractData(ClientHttpResponse response) throws IOException {

        MyEntity result;

        if (response.getStatusCode() == HttpStatus.OK) {
            result = super.extractData(response);
        } else {
            result = null;
        }

        return result;
    }
}

我已经测试过这个,它似乎做了我想做的事。

为了创建ReactExtractor的实例,我调用构造函数并从已注入的RestTemplate实例传递转换器;

例如:

ResponseExtractor<MyEntity> responseExtractor =
    new MyResponseExtractor(MyEntity.class, restTemplate.getMessageConverters());

然后调用是:

MyEntity responseAsEntity =
    restTemplate.execute(urlToCall, HttpMethod.GET, null, responseExtractor);

您的里程可能会有所不同。;-)


推荐