このコードでスプリングを使用する:
List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
for(HttpMessageConverter httpMessageConverter : messageConverters){
System.out.println(httpMessageConverter);
}
ResponseEntity<ProductList> productList = restTemplate.getForEntity(productDataUrl,ProductList.class);
私は得る
org.springframework.http.converter.ByteArrayHttpMessageConverter@34649ee4
org.springframework.http.converter.StringHttpMessageConverter@39fba59b
org.springframework.http.converter.ResourceHttpMessageConverter@383580da
org.springframework.http.converter.xml.SourceHttpMessageConverter@409e850a
org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter@673074aa
org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter@1e3b79d3
org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@52bb1b26
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class com.mycopmany.ProductList] and content type [text/html;charset=UTF-8]
Pojoのスニペット:
@XmlRootElement(name="TheProductList")
public class ProductList {
@XmlElement(required = true, name = "date")
private LocalDate importDate;
Springの観点からすると、HttpMessageConverter
に登録されているRestTemplate
インスタンスは、text/html
コンテンツをProductList
オブジェクトに変換できません。対象のメソッドはHttpMessageConverter#canRead(Class, MediaType)
です。上記のすべての実装は、Jaxb2RootElementHttpMessageConverter
を含むfalse
を返します。
HttpMessageConverter
はHTTP応答を読み取ることができないため、処理は例外で失敗します。
サーバーの応答を制御できる場合は、Content-type
をapplication/xml
、text/xml
、またはapplication/*+xml
に一致するものに設定するように変更します。
サーバーの応答を制御しない場合は、text/html
を読み取りおよび変換できる独自のHttpMessageConverter
(Springクラスを拡張できる、AbstractXmlHttpMessageConverter
およびそのサブクラスを参照)を作成および登録する必要があります。 。
サーバーのメディアタイプの応答を変更できない場合は、GsonHttpMessageConverterを拡張して追加のサポートタイプを処理できます
public class MyGsonHttpMessageConverter extends GsonHttpMessageConverter {
public MyGsonHttpMessageConverter() {
List<MediaType> types = Arrays.asList(
new MediaType("text", "html", DEFAULT_CHARSET),
new MediaType("application", "json", DEFAULT_CHARSET),
new MediaType("application", "*+json", DEFAULT_CHARSET)
);
super.setSupportedMediaTypes(types);
}
}
Spring Bootを使用している場合は、クラスパスにJackson依存関係があることを確認する必要があります。これは、次の方法で手動で実行できます。
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
または、Webスターターを使用できます。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
RestTemplateを拡張するRestTemplateXMLクラスを作成できます。次に、doExecute(URI, HttpMethod, RequestCallback, ResponseExtractor<T>)
をオーバーライドし、明示的にresponse-headers
を取得し、content-type
をapplication/xml
に設定します。
これで、Springはヘッダーを読み取り、それが「application/xml」であることを認識します。これは一種のハックですが、動作します。
public class RestTemplateXML extends RestTemplate {
@Override
protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback,
ResponseExtractor<T> responseExtractor) throws RestClientException {
logger.info( RestTemplateXML.class.getSuperclass().getSimpleName() + ".doExecute() is overridden");
Assert.notNull(url, "'url' must not be null");
Assert.notNull(method, "'method' must not be null");
ClientHttpResponse response = null;
try {
ClientHttpRequest request = createRequest(url, method);
if (requestCallback != null) {
requestCallback.doWithRequest(request);
}
response = request.execute();
// Set ContentType to XML
response.getHeaders().setContentType(MediaType.APPLICATION_XML);
if (!getErrorHandler().hasError(response)) {
logResponseStatus(method, url, response);
}
else {
handleResponseError(method, url, response);
}
if (responseExtractor != null) {
return responseExtractor.extractData(response);
}
else {
return null;
}
}
catch (IOException ex) {
throw new ResourceAccessException("I/O error on " + method.name() +
" request for \"" + url + "\":" + ex.getMessage(), ex);
}
finally {
if (response != null) {
response.close();
}
}
}
private void logResponseStatus(HttpMethod method, URI url, ClientHttpResponse response) {
if (logger.isDebugEnabled()) {
try {
logger.debug(method.name() + " request for \"" + url + "\" resulted in " +
response.getRawStatusCode() + " (" + response.getStatusText() + ")");
}
catch (IOException e) {
// ignore
}
}
}
private void handleResponseError(HttpMethod method, URI url, ClientHttpResponse response) throws IOException {
if (logger.isWarnEnabled()) {
try {
logger.warn(method.name() + " request for \"" + url + "\" resulted in " +
response.getRawStatusCode() + " (" + response.getStatusText() + "); invoking error handler");
}
catch (IOException e) {
// ignore
}
}
getErrorHandler().handleError(response);
}
}
すべての回答に加えて、何か他のもの(つまりtext/html
)を期待しているときに応答application/json
を受け取った場合、サーバー側でエラーが発生したことを示唆することがあります(たとえば404)データの代わりにエラーページが返されました。
それで私の場合はそうなった。それが誰かの時間を節約することを願っています。
RestTemplate
にすべてのメディアタイプを受け入れるように指示することもできます。
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
final RestTemplate restTemplate = new RestTemplate();
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setSupportedMediaTypes(Collections.singletonList(MediaType.ALL));
messageConverters.add(converter);
restTemplate.setMessageConverters(messageConverters);
return restTemplate;
}
または、使用できます
public void setSupportedMediaTypes(List supportedMediaTypes)
AbstractHttpMessageConverter<T>
に属するメソッドで、好きなContentTypes
を追加します。この方法により、MappingJackson2HttpMessageConverter
canRead()
に応答を許可し、それを目的のクラス(この場合はProductListクラス)に変換できます。
そして、このステップはSpring Contextの初期化に結び付けられるべきだと思います。たとえば、次を使用して
applicationListenerを実装します{...}
これを試して:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.6.0</version>
</dependency>
Vadim Zin4uk's answer の改良点は、既存のGsonHttpMessageConverterクラスを使用するだけで、setSupportedMediaTypes()セッターを呼び出すことです。
スプリングブートアプリの場合、これは構成クラスに以下を追加することになります。
@Bean
public GsonHttpMessageConverter gsonHttpMessageConverter(Gson gson) {
GsonHttpMessageConverter converter = new GsonHttpMessageConverter();
converter.setGson(gson);
List<MediaType> supportedMediaTypes = converter.getSupportedMediaTypes();
if (! supportedMediaTypes.contains(TEXT_PLAIN)) {
supportedMediaTypes = new ArrayList<>(supportedMediaTypes);
supportedMediaTypes.add(TEXT_PLAIN);
converter.setSupportedMediaTypes(supportedMediaTypes);
}
return converter;
}