OK、クラスNamedSystemsがあり、唯一のフィールドとしてNamedSystemのセットがあります。
特定の基準でNamedSystemsを検索する方法があります。それはそれほど重要ではありません。結果が出ると、すべて正常に動作します。ただし、何も見つからず、null(または空-両方の方法を試した)セットを返す場合、問題が発生します。説明させてください。
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 + "]");
}
チェーンを少し上に移動して、Extractorが設定されている場所を確認すると、テストで使用した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);
}
したがって、exchange呼び出しから提供された応答タイプのために、何を意味しないものに変換しようとしています。 responseTypeをNamedSystems.classからnullに変更すると、期待どおりに機能します。何も変換しようとしません。ステータスコードを404に設定しようとした場合も、正常に実行されます。
私は見当違いですか、それともRestTemplateの欠陥のように見えますか?確かに、私は今junitを使用しているので、何が起こるかはわかっていますが、誰かがRestTemplateを使用してこれを呼び出しており、サービス呼び出しの結果がわからない場合は、当然、応答タイプとしてNamedSystemsがあります。ただし、要素が見つからない条件検索を試行した場合、この厄介なエラーが発生します。
RestTemplateのものをオーバーライドせずにこれを回避する方法はありますか?この状況を誤って表示していますか?私は少し困惑しているので助けてください。
これは、Spring 3.1 RC1で修正されています。
これを解決するもう1つの方法は、以下に示すように、応答エンティティをnullにすることです。
ResponseEntity<?> response = restTemplate.exchange("http://localhost:8080/myapp/user/{userID}", HttpMethod.DELETE, requestEntity, null, userID);
それでも応答ヘッダーが必要な場合は、ResponseErrorHandlerを実装してみてください。
おそらく、Extractorの実装を提供するRestTemplateでResponseExtractorインターフェイスを呼び出してexecuteを呼び出す必要があると思います。私にはこれを行うための一般的な要件のように見えるので、これをログに記録しました:
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;
}
}
私はこれをテストしました、そしてそれは私が望むことをするようです。
ResponseExtractorのインスタンスを作成するには、コンストラクターを呼び出して、注入されたRestTemplateインスタンスからコンバーターを渡します。
例えば。
ResponseExtractor<MyEntity> responseExtractor =
new MyResponseExtractor(MyEntity.class, restTemplate.getMessageConverters());
次に、呼び出しは次のとおりです。
MyEntity responseAsEntity =
restTemplate.execute(urlToCall, HttpMethod.GET, null, responseExtractor);
あなたのマイレージは異なる場合があります。 ;-)
これは、応答に欠落している場合に使用するデフォルトのContent-Typeを設定できる簡単なソリューションです。 Content-Typeは、事前設定されたResponseExtractorに抽出のために渡される前に、応答ヘッダーに追加されます。
public class CustomRestTemplate extends RestTemplate {
private MediaType defaultResponseContentType;
public CustomRestTemplate() {
super();
}
public CustomRestTemplate(ClientHttpRequestFactory requestFactory) {
super(requestFactory);
}
public void setDefaultResponseContentType(String defaultResponseContentType) {
this.defaultResponseContentType = MediaType.parseMediaType(defaultResponseContentType);
}
@Override
protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback, final ResponseExtractor<T> responseExtractor)
throws RestClientException {
return super.doExecute(url, method, requestCallback, new ResponseExtractor<T>() {
public T extractData(ClientHttpResponse response) throws IOException {
if (response.getHeaders().getContentType() == null && defaultResponseContentType != null) {
response.getHeaders().setContentType(defaultResponseContentType);
}
return responseExtractor.extractData(response);
}
});
}
}
私はあなたが正しいと思います。同様の問題が発生しています。 HttpStatusがNO_CONTENTで、本文がnullのResponseEntityを取得する必要があると思います。
または、RestTemplateを拡張してdoExecute(..)をオーバーライドし、応答本文を確認することもできます。
たとえば、ここに私が実装して私たちのために働いているものがあります:
@Override
protected <T> T doExecute(final URI url, final HttpMethod method, final RequestCallback requestCallback, final ResponseExtractor<T> responseExtractor)
throws RestClientException
{
Assert.notNull(url, "'url' must not be null");
Assert.notNull(method, "'method' must not be null");
ClientHttpResponse response = null;
try
{
final ClientHttpRequest request = createRequest(url, method);
if (requestCallback != null)
{
requestCallback.doWithRequest(request);
}
response = request.execute();
if (!getErrorHandler().hasError(response))
{
logResponseStatus(method, url, response);
}
else
{
handleResponseError(method, url, response);
}
if ((response.getBody() == null) || (responseExtractor == null))
{
return null;
}
return responseExtractor.extractData(response);
}
catch (final IOException ex)
{
throw new ResourceAccessException("I/O error: " + ex.getMessage(), ex);
}
finally
{
if (response != null)
{
response.close();
}
}
}