All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.web.client.RestTemplate Maven / Gradle / Ivy

There is a newer version: 6.1.6
Show newest version
/*
 * Copyright 2002-2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.web.client;

import java.io.IOException;
import java.lang.reflect.Type;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.transform.Source;

import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.support.InterceptingHttpAccessor;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.GenericHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.ResourceHttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.feed.AtomFeedHttpMessageConverter;
import org.springframework.http.converter.feed.RssChannelHttpMessageConverter;
import org.springframework.http.converter.json.GsonHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.web.util.AbstractUriTemplateHandler;
import org.springframework.web.util.DefaultUriTemplateHandler;
import org.springframework.web.util.UriTemplateHandler;

/**
 * Spring's central class for synchronous client-side HTTP access.
 * It simplifies communication with HTTP servers, and enforces RESTful principles.
 * It handles HTTP connections, leaving application code to provide URLs
 * (with possible template variables) and extract results.
 *
 * 

Note: by default the RestTemplate relies on standard JDK * facilities to establish HTTP connections. You can switch to use a different * HTTP library such as Apache HttpComponents, Netty, and OkHttp through the * {@link #setRequestFactory} property. * *

The main entry points of this template are the methods named after the six main HTTP methods: *

* * * * * * * * * * *
HTTP methodRestTemplate methods
DELETE{@link #delete}
GET{@link #getForObject}
{@link #getForEntity}
HEAD{@link #headForHeaders}
OPTIONS{@link #optionsForAllow}
POST{@link #postForLocation}
{@link #postForObject}
PUT{@link #put}
any{@link #exchange}
{@link #execute}
* *

In addition the {@code exchange} and {@code execute} methods are generalized versions of * the above methods and can be used to support additional, less frequent combinations (e.g. * HTTP PATCH, HTTP PUT with response body, etc.). Note however that the underlying HTTP * library used must also support the desired combination. * *

For each HTTP method there are three variants: two accept a URI template string * and URI variables (array or map) while a third accepts a {@link URI}. * Note that for URI templates it is assumed encoding is necessary, e.g. * {@code restTemplate.getForObject("http://example.com/hotel list")} becomes * {@code "http://example.com/hotel%20list"}. This also means if the URI template * or URI variables are already encoded, double encoding will occur, e.g. * {@code http://example.com/hotel%20list} becomes * {@code http://example.com/hotel%2520list}). To avoid that use a {@code URI} method * variant to provide (or re-use) a previously encoded URI. To prepare such an URI * with full control over encoding, consider using * {@link org.springframework.web.util.UriComponentsBuilder}. * *

Internally the template uses {@link HttpMessageConverter} instances to * convert HTTP messages to and from POJOs. Converters for the main mime types * are registered by default but you can also register additional converters * via {@link #setMessageConverters}. * *

This template uses a * {@link org.springframework.http.client.SimpleClientHttpRequestFactory} and a * {@link DefaultResponseErrorHandler} as default strategies for creating HTTP * connections or handling HTTP errors, respectively. These defaults can be overridden * through {@link #setRequestFactory} and {@link #setErrorHandler} respectively. * * @author Arjen Poutsma * @author Brian Clozel * @author Roy Clarkson * @author Juergen Hoeller * @since 3.0 * @see HttpMessageConverter * @see RequestCallback * @see ResponseExtractor * @see ResponseErrorHandler * @see AsyncRestTemplate */ public class RestTemplate extends InterceptingHttpAccessor implements RestOperations { private static boolean romePresent = ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", RestTemplate.class.getClassLoader()); private static final boolean jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", RestTemplate.class.getClassLoader()); private static final boolean jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", RestTemplate.class.getClassLoader()) && ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", RestTemplate.class.getClassLoader()); private static final boolean jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", RestTemplate.class.getClassLoader()); private static final boolean gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", RestTemplate.class.getClassLoader()); private final List> messageConverters = new ArrayList>(); private ResponseErrorHandler errorHandler = new DefaultResponseErrorHandler(); private UriTemplateHandler uriTemplateHandler = new DefaultUriTemplateHandler(); private final ResponseExtractor headersExtractor = new HeadersExtractor(); /** * Create a new instance of the {@link RestTemplate} using default settings. * Default {@link HttpMessageConverter}s are initialized. */ public RestTemplate() { this.messageConverters.add(new ByteArrayHttpMessageConverter()); this.messageConverters.add(new StringHttpMessageConverter()); this.messageConverters.add(new ResourceHttpMessageConverter()); this.messageConverters.add(new SourceHttpMessageConverter()); this.messageConverters.add(new AllEncompassingFormHttpMessageConverter()); if (romePresent) { this.messageConverters.add(new AtomFeedHttpMessageConverter()); this.messageConverters.add(new RssChannelHttpMessageConverter()); } if (jackson2XmlPresent) { this.messageConverters.add(new MappingJackson2XmlHttpMessageConverter()); } else if (jaxb2Present) { this.messageConverters.add(new Jaxb2RootElementHttpMessageConverter()); } if (jackson2Present) { this.messageConverters.add(new MappingJackson2HttpMessageConverter()); } else if (gsonPresent) { this.messageConverters.add(new GsonHttpMessageConverter()); } } /** * Create a new instance of the {@link RestTemplate} based on the given {@link ClientHttpRequestFactory}. * @param requestFactory HTTP request factory to use * @see org.springframework.http.client.SimpleClientHttpRequestFactory * @see org.springframework.http.client.HttpComponentsClientHttpRequestFactory */ public RestTemplate(ClientHttpRequestFactory requestFactory) { this(); setRequestFactory(requestFactory); } /** * Create a new instance of the {@link RestTemplate} using the given list of * {@link HttpMessageConverter} to use * @param messageConverters the list of {@link HttpMessageConverter} to use * @since 3.2.7 */ public RestTemplate(List> messageConverters) { Assert.notEmpty(messageConverters, "At least one HttpMessageConverter required"); this.messageConverters.addAll(messageConverters); } /** * Set the message body converters to use. *

These converters are used to convert from and to HTTP requests and responses. */ public void setMessageConverters(List> messageConverters) { Assert.notEmpty(messageConverters, "At least one HttpMessageConverter required"); // Take getMessageConverters() List as-is when passed in here if (this.messageConverters != messageConverters) { this.messageConverters.clear(); this.messageConverters.addAll(messageConverters); } } /** * Return the message body converters. */ public List> getMessageConverters() { return this.messageConverters; } /** * Set the error handler. *

By default, RestTemplate uses a {@link DefaultResponseErrorHandler}. */ public void setErrorHandler(ResponseErrorHandler errorHandler) { Assert.notNull(errorHandler, "ResponseErrorHandler must not be null"); this.errorHandler = errorHandler; } /** * Return the error handler. */ public ResponseErrorHandler getErrorHandler() { return this.errorHandler; } /** * Configure default URI variable values. This is a shortcut for: *

	 * DefaultUriTemplateHandler handler = new DefaultUriTemplateHandler();
	 * handler.setDefaultUriVariables(...);
	 *
	 * RestTemplate restTemplate = new RestTemplate();
	 * restTemplate.setUriTemplateHandler(handler);
	 * 
* @param defaultUriVariables the default URI variable values * @since 4.3 */ public void setDefaultUriVariables(Map defaultUriVariables) { Assert.isInstanceOf(AbstractUriTemplateHandler.class, this.uriTemplateHandler, "Can only use this property in conjunction with an AbstractUriTemplateHandler"); ((AbstractUriTemplateHandler) this.uriTemplateHandler).setDefaultUriVariables(defaultUriVariables); } /** * Configure the {@link UriTemplateHandler} to use to expand URI templates. * By default the {@link DefaultUriTemplateHandler} is used which relies on * Spring's URI template support and exposes several useful properties that * customize its behavior for encoding and for prepending a common base URL. * An alternative implementation may be used to plug an external URI * template library. * @param handler the URI template handler to use */ public void setUriTemplateHandler(UriTemplateHandler handler) { Assert.notNull(handler, "UriTemplateHandler must not be null"); this.uriTemplateHandler = handler; } /** * Return the configured URI template handler. */ public UriTemplateHandler getUriTemplateHandler() { return this.uriTemplateHandler; } // GET @Override public T getForObject(String url, Class responseType, Object... uriVariables) throws RestClientException { RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); HttpMessageConverterExtractor responseExtractor = new HttpMessageConverterExtractor(responseType, getMessageConverters(), logger); return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables); } @Override public T getForObject(String url, Class responseType, Map uriVariables) throws RestClientException { RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); HttpMessageConverterExtractor responseExtractor = new HttpMessageConverterExtractor(responseType, getMessageConverters(), logger); return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables); } @Override public T getForObject(URI url, Class responseType) throws RestClientException { RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); HttpMessageConverterExtractor responseExtractor = new HttpMessageConverterExtractor(responseType, getMessageConverters(), logger); return execute(url, HttpMethod.GET, requestCallback, responseExtractor); } @Override public ResponseEntity getForEntity(String url, Class responseType, Object... uriVariables) throws RestClientException { RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); ResponseExtractor> responseExtractor = responseEntityExtractor(responseType); return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables); } @Override public ResponseEntity getForEntity(String url, Class responseType, Map uriVariables) throws RestClientException { RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); ResponseExtractor> responseExtractor = responseEntityExtractor(responseType); return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables); } @Override public ResponseEntity getForEntity(URI url, Class responseType) throws RestClientException { RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); ResponseExtractor> responseExtractor = responseEntityExtractor(responseType); return execute(url, HttpMethod.GET, requestCallback, responseExtractor); } // HEAD @Override public HttpHeaders headForHeaders(String url, Object... uriVariables) throws RestClientException { return execute(url, HttpMethod.HEAD, null, headersExtractor(), uriVariables); } @Override public HttpHeaders headForHeaders(String url, Map uriVariables) throws RestClientException { return execute(url, HttpMethod.HEAD, null, headersExtractor(), uriVariables); } @Override public HttpHeaders headForHeaders(URI url) throws RestClientException { return execute(url, HttpMethod.HEAD, null, headersExtractor()); } // POST @Override public URI postForLocation(String url, Object request, Object... uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request); HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, headersExtractor(), uriVariables); return headers.getLocation(); } @Override public URI postForLocation(String url, Object request, Map uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request); HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, headersExtractor(), uriVariables); return headers.getLocation(); } @Override public URI postForLocation(URI url, Object request) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request); HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, headersExtractor()); return headers.getLocation(); } @Override public T postForObject(String url, Object request, Class responseType, Object... uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request, responseType); HttpMessageConverterExtractor responseExtractor = new HttpMessageConverterExtractor(responseType, getMessageConverters(), logger); return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables); } @Override public T postForObject(String url, Object request, Class responseType, Map uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request, responseType); HttpMessageConverterExtractor responseExtractor = new HttpMessageConverterExtractor(responseType, getMessageConverters(), logger); return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables); } @Override public T postForObject(URI url, Object request, Class responseType) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request, responseType); HttpMessageConverterExtractor responseExtractor = new HttpMessageConverterExtractor(responseType, getMessageConverters()); return execute(url, HttpMethod.POST, requestCallback, responseExtractor); } @Override public ResponseEntity postForEntity(String url, Object request, Class responseType, Object... uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request, responseType); ResponseExtractor> responseExtractor = responseEntityExtractor(responseType); return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables); } @Override public ResponseEntity postForEntity(String url, Object request, Class responseType, Map uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request, responseType); ResponseExtractor> responseExtractor = responseEntityExtractor(responseType); return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables); } @Override public ResponseEntity postForEntity(URI url, Object request, Class responseType) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request, responseType); ResponseExtractor> responseExtractor = responseEntityExtractor(responseType); return execute(url, HttpMethod.POST, requestCallback, responseExtractor); } // PUT @Override public void put(String url, Object request, Object... uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request); execute(url, HttpMethod.PUT, requestCallback, null, uriVariables); } @Override public void put(String url, Object request, Map uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request); execute(url, HttpMethod.PUT, requestCallback, null, uriVariables); } @Override public void put(URI url, Object request) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request); execute(url, HttpMethod.PUT, requestCallback, null); } // PATCH @Override public T patchForObject(String url, Object request, Class responseType, Object... uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request, responseType); HttpMessageConverterExtractor responseExtractor = new HttpMessageConverterExtractor(responseType, getMessageConverters(), logger); return execute(url, HttpMethod.PATCH, requestCallback, responseExtractor, uriVariables); } @Override public T patchForObject(String url, Object request, Class responseType, Map uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request, responseType); HttpMessageConverterExtractor responseExtractor = new HttpMessageConverterExtractor(responseType, getMessageConverters(), logger); return execute(url, HttpMethod.PATCH, requestCallback, responseExtractor, uriVariables); } @Override public T patchForObject(URI url, Object request, Class responseType) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request, responseType); HttpMessageConverterExtractor responseExtractor = new HttpMessageConverterExtractor(responseType, getMessageConverters()); return execute(url, HttpMethod.PATCH, requestCallback, responseExtractor); } // DELETE @Override public void delete(String url, Object... uriVariables) throws RestClientException { execute(url, HttpMethod.DELETE, null, null, uriVariables); } @Override public void delete(String url, Map uriVariables) throws RestClientException { execute(url, HttpMethod.DELETE, null, null, uriVariables); } @Override public void delete(URI url) throws RestClientException { execute(url, HttpMethod.DELETE, null, null); } // OPTIONS @Override public Set optionsForAllow(String url, Object... uriVariables) throws RestClientException { ResponseExtractor headersExtractor = headersExtractor(); HttpHeaders headers = execute(url, HttpMethod.OPTIONS, null, headersExtractor, uriVariables); return headers.getAllow(); } @Override public Set optionsForAllow(String url, Map uriVariables) throws RestClientException { ResponseExtractor headersExtractor = headersExtractor(); HttpHeaders headers = execute(url, HttpMethod.OPTIONS, null, headersExtractor, uriVariables); return headers.getAllow(); } @Override public Set optionsForAllow(URI url) throws RestClientException { ResponseExtractor headersExtractor = headersExtractor(); HttpHeaders headers = execute(url, HttpMethod.OPTIONS, null, headersExtractor); return headers.getAllow(); } // exchange @Override public ResponseEntity exchange(String url, HttpMethod method, HttpEntity requestEntity, Class responseType, Object... uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType); ResponseExtractor> responseExtractor = responseEntityExtractor(responseType); return execute(url, method, requestCallback, responseExtractor, uriVariables); } @Override public ResponseEntity exchange(String url, HttpMethod method, HttpEntity requestEntity, Class responseType, Map uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType); ResponseExtractor> responseExtractor = responseEntityExtractor(responseType); return execute(url, method, requestCallback, responseExtractor, uriVariables); } @Override public ResponseEntity exchange(URI url, HttpMethod method, HttpEntity requestEntity, Class responseType) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType); ResponseExtractor> responseExtractor = responseEntityExtractor(responseType); return execute(url, method, requestCallback, responseExtractor); } @Override public ResponseEntity exchange(String url, HttpMethod method, HttpEntity requestEntity, ParameterizedTypeReference responseType, Object... uriVariables) throws RestClientException { Type type = responseType.getType(); RequestCallback requestCallback = httpEntityCallback(requestEntity, type); ResponseExtractor> responseExtractor = responseEntityExtractor(type); return execute(url, method, requestCallback, responseExtractor, uriVariables); } @Override public ResponseEntity exchange(String url, HttpMethod method, HttpEntity requestEntity, ParameterizedTypeReference responseType, Map uriVariables) throws RestClientException { Type type = responseType.getType(); RequestCallback requestCallback = httpEntityCallback(requestEntity, type); ResponseExtractor> responseExtractor = responseEntityExtractor(type); return execute(url, method, requestCallback, responseExtractor, uriVariables); } @Override public ResponseEntity exchange(URI url, HttpMethod method, HttpEntity requestEntity, ParameterizedTypeReference responseType) throws RestClientException { Type type = responseType.getType(); RequestCallback requestCallback = httpEntityCallback(requestEntity, type); ResponseExtractor> responseExtractor = responseEntityExtractor(type); return execute(url, method, requestCallback, responseExtractor); } @Override public ResponseEntity exchange(RequestEntity requestEntity, Class responseType) throws RestClientException { Assert.notNull(requestEntity, "'requestEntity' must not be null"); RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType); ResponseExtractor> responseExtractor = responseEntityExtractor(responseType); return execute(requestEntity.getUrl(), requestEntity.getMethod(), requestCallback, responseExtractor); } @Override public ResponseEntity exchange(RequestEntity requestEntity, ParameterizedTypeReference responseType) throws RestClientException { Assert.notNull(requestEntity, "'requestEntity' must not be null"); Type type = responseType.getType(); RequestCallback requestCallback = httpEntityCallback(requestEntity, type); ResponseExtractor> responseExtractor = responseEntityExtractor(type); return execute(requestEntity.getUrl(), requestEntity.getMethod(), requestCallback, responseExtractor); } // general execution @Override public T execute(String url, HttpMethod method, RequestCallback requestCallback, ResponseExtractor responseExtractor, Object... uriVariables) throws RestClientException { URI expanded = getUriTemplateHandler().expand(url, uriVariables); return doExecute(expanded, method, requestCallback, responseExtractor); } @Override public T execute(String url, HttpMethod method, RequestCallback requestCallback, ResponseExtractor responseExtractor, Map uriVariables) throws RestClientException { URI expanded = getUriTemplateHandler().expand(url, uriVariables); return doExecute(expanded, method, requestCallback, responseExtractor); } @Override public T execute(URI url, HttpMethod method, RequestCallback requestCallback, ResponseExtractor responseExtractor) throws RestClientException { return doExecute(url, method, requestCallback, responseExtractor); } /** * Execute the given method on the provided URI. *

The {@link ClientHttpRequest} is processed using the {@link RequestCallback}; * the response with the {@link ResponseExtractor}. * @param url the fully-expanded URL to connect to * @param method the HTTP method to execute (GET, POST, etc.) * @param requestCallback object that prepares the request (can be {@code null}) * @param responseExtractor object that extracts the return value from the response (can be {@code null}) * @return an arbitrary object, as returned by the {@link ResponseExtractor} */ protected T doExecute(URI url, HttpMethod method, RequestCallback requestCallback, ResponseExtractor responseExtractor) throws RestClientException { 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(); handleResponse(url, method, response); if (responseExtractor != null) { return responseExtractor.extractData(response); } else { return null; } } catch (IOException ex) { String resource = url.toString(); String query = url.getRawQuery(); resource = (query != null ? resource.substring(0, resource.indexOf(query) - 1) : resource); throw new ResourceAccessException("I/O error on " + method.name() + " request for \"" + resource + "\": " + ex.getMessage(), ex); } finally { if (response != null) { response.close(); } } } /** * Handle the given response, performing appropriate logging and * invoking the {@link ResponseErrorHandler} if necessary. *

Can be overridden in subclasses. * @param url the fully-expanded URL to connect to * @param method the HTTP method to execute (GET, POST, etc.) * @param response the resulting {@link ClientHttpResponse} * @throws IOException if propagated from {@link ResponseErrorHandler} * @since 4.1.6 * @see #setErrorHandler */ protected void handleResponse(URI url, HttpMethod method, ClientHttpResponse response) throws IOException { ResponseErrorHandler errorHandler = getErrorHandler(); boolean hasError = errorHandler.hasError(response); if (logger.isDebugEnabled()) { try { logger.debug(method.name() + " request for \"" + url + "\" resulted in " + response.getRawStatusCode() + " (" + response.getStatusText() + ")" + (hasError ? "; invoking error handler" : "")); } catch (IOException ex) { // ignore } } if (hasError) { errorHandler.handleError(response); } } /** * Returns a request callback implementation that prepares the request {@code Accept} * headers based on the given response type and configured * {@linkplain #getMessageConverters() message converters}. */ protected RequestCallback acceptHeaderRequestCallback(Class responseType) { return new AcceptHeaderRequestCallback(responseType); } /** * Returns a request callback implementation that writes the given object to the * request stream. */ protected RequestCallback httpEntityCallback(Object requestBody) { return new HttpEntityRequestCallback(requestBody); } /** * Returns a request callback implementation that writes the given object to the * request stream. */ protected RequestCallback httpEntityCallback(Object requestBody, Type responseType) { return new HttpEntityRequestCallback(requestBody, responseType); } /** * Returns a response extractor for {@link ResponseEntity}. */ protected ResponseExtractor> responseEntityExtractor(Type responseType) { return new ResponseEntityResponseExtractor(responseType); } /** * Returns a response extractor for {@link HttpHeaders}. */ protected ResponseExtractor headersExtractor() { return this.headersExtractor; } /** * Request callback implementation that prepares the request's accept headers. */ private class AcceptHeaderRequestCallback implements RequestCallback { private final Type responseType; private AcceptHeaderRequestCallback(Type responseType) { this.responseType = responseType; } @Override public void doWithRequest(ClientHttpRequest request) throws IOException { if (this.responseType != null) { Class responseClass = null; if (this.responseType instanceof Class) { responseClass = (Class) this.responseType; } List allSupportedMediaTypes = new ArrayList(); for (HttpMessageConverter converter : getMessageConverters()) { if (responseClass != null) { if (converter.canRead(responseClass, null)) { allSupportedMediaTypes.addAll(getSupportedMediaTypes(converter)); } } else if (converter instanceof GenericHttpMessageConverter) { GenericHttpMessageConverter genericConverter = (GenericHttpMessageConverter) converter; if (genericConverter.canRead(this.responseType, null, null)) { allSupportedMediaTypes.addAll(getSupportedMediaTypes(converter)); } } } if (!allSupportedMediaTypes.isEmpty()) { MediaType.sortBySpecificity(allSupportedMediaTypes); if (logger.isDebugEnabled()) { logger.debug("Setting request Accept header to " + allSupportedMediaTypes); } request.getHeaders().setAccept(allSupportedMediaTypes); } } } private List getSupportedMediaTypes(HttpMessageConverter messageConverter) { List supportedMediaTypes = messageConverter.getSupportedMediaTypes(); List result = new ArrayList(supportedMediaTypes.size()); for (MediaType supportedMediaType : supportedMediaTypes) { if (supportedMediaType.getCharset() != null) { supportedMediaType = new MediaType(supportedMediaType.getType(), supportedMediaType.getSubtype()); } result.add(supportedMediaType); } return result; } } /** * Request callback implementation that writes the given object to the request stream. */ private class HttpEntityRequestCallback extends AcceptHeaderRequestCallback { private final HttpEntity requestEntity; private HttpEntityRequestCallback(Object requestBody) { this(requestBody, null); } private HttpEntityRequestCallback(Object requestBody, Type responseType) { super(responseType); if (requestBody instanceof HttpEntity) { this.requestEntity = (HttpEntity) requestBody; } else if (requestBody != null) { this.requestEntity = new HttpEntity(requestBody); } else { this.requestEntity = HttpEntity.EMPTY; } } @Override @SuppressWarnings("unchecked") public void doWithRequest(ClientHttpRequest httpRequest) throws IOException { super.doWithRequest(httpRequest); if (!this.requestEntity.hasBody()) { HttpHeaders httpHeaders = httpRequest.getHeaders(); HttpHeaders requestHeaders = this.requestEntity.getHeaders(); if (!requestHeaders.isEmpty()) { httpHeaders.putAll(requestHeaders); } if (httpHeaders.getContentLength() < 0) { httpHeaders.setContentLength(0L); } } else { Object requestBody = this.requestEntity.getBody(); Class requestBodyClass = requestBody.getClass(); Type requestBodyType = (this.requestEntity instanceof RequestEntity ? ((RequestEntity)this.requestEntity).getType() : requestBodyClass); HttpHeaders requestHeaders = this.requestEntity.getHeaders(); MediaType requestContentType = requestHeaders.getContentType(); for (HttpMessageConverter messageConverter : getMessageConverters()) { if (messageConverter instanceof GenericHttpMessageConverter) { GenericHttpMessageConverter genericMessageConverter = (GenericHttpMessageConverter) messageConverter; if (genericMessageConverter.canWrite(requestBodyType, requestBodyClass, requestContentType)) { if (!requestHeaders.isEmpty()) { httpRequest.getHeaders().putAll(requestHeaders); } if (logger.isDebugEnabled()) { if (requestContentType != null) { logger.debug("Writing [" + requestBody + "] as \"" + requestContentType + "\" using [" + messageConverter + "]"); } else { logger.debug("Writing [" + requestBody + "] using [" + messageConverter + "]"); } } genericMessageConverter.write( requestBody, requestBodyType, requestContentType, httpRequest); return; } } else if (messageConverter.canWrite(requestBodyClass, requestContentType)) { if (!requestHeaders.isEmpty()) { httpRequest.getHeaders().putAll(requestHeaders); } if (logger.isDebugEnabled()) { if (requestContentType != null) { logger.debug("Writing [" + requestBody + "] as \"" + requestContentType + "\" using [" + messageConverter + "]"); } else { logger.debug("Writing [" + requestBody + "] using [" + messageConverter + "]"); } } ((HttpMessageConverter) messageConverter).write( requestBody, requestContentType, httpRequest); return; } } String message = "Could not write request: no suitable HttpMessageConverter found for request type [" + requestBodyClass.getName() + "]"; if (requestContentType != null) { message += " and content type [" + requestContentType + "]"; } throw new RestClientException(message); } } } /** * Response extractor for {@link HttpEntity}. */ private class ResponseEntityResponseExtractor implements ResponseExtractor> { private final HttpMessageConverterExtractor delegate; public ResponseEntityResponseExtractor(Type responseType) { if (responseType != null && Void.class != responseType) { this.delegate = new HttpMessageConverterExtractor(responseType, getMessageConverters(), logger); } else { this.delegate = null; } } @Override public ResponseEntity extractData(ClientHttpResponse response) throws IOException { if (this.delegate != null) { T body = this.delegate.extractData(response); return new ResponseEntity(body, response.getHeaders(), response.getStatusCode()); } else { return new ResponseEntity(response.getHeaders(), response.getStatusCode()); } } } /** * Response extractor that extracts the response {@link HttpHeaders}. */ private static class HeadersExtractor implements ResponseExtractor { @Override public HttpHeaders extractData(ClientHttpResponse response) throws IOException { return response.getHeaders(); } } }