/*
 * Copyright 2002-2019 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.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
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.cbor.MappingJackson2CborHttpMessageConverter;
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.JsonbHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.smile.MappingJackson2SmileHttpMessageConverter;
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.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.web.util.DefaultUriBuilderFactory;
import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode;
import org.springframework.web.util.UriTemplateHandler;

Synchronous client to perform HTTP requests, exposing a simple, template method API over underlying HTTP client libraries such as the JDK HttpURLConnection, Apache HttpComponents, and others.

The RestTemplate offers templates for common scenarios by HTTP method, in addition to the generalized exchange and execute methods that support of less frequent cases.

NOTE: As of 5.0, the non-blocking, reactive org.springframework.web.reactive.client.WebClient offers a modern alternative to the RestTemplate with efficient support for both sync and async, as well as streaming scenarios. The RestTemplate will be deprecated in a future version and will not have major new features added going forward. See the WebClient section of the Spring Framework reference documentation for more details and example code.

Author:Arjen Poutsma, Brian Clozel, Roy Clarkson, Juergen Hoeller
See Also:
Since:3.0
/** * Synchronous client to perform HTTP requests, exposing a simple, template * method API over underlying HTTP client libraries such as the JDK * {@code HttpURLConnection}, Apache HttpComponents, and others. * * <p>The RestTemplate offers templates for common scenarios by HTTP method, in * addition to the generalized {@code exchange} and {@code execute} methods that * support of less frequent cases. * * <p><strong>NOTE:</strong> As of 5.0, the non-blocking, reactive * {@code org.springframework.web.reactive.client.WebClient} offers a * modern alternative to the {@code RestTemplate} with efficient support for * both sync and async, as well as streaming scenarios. The {@code RestTemplate} * will be deprecated in a future version and will not have major new features * added going forward. See the WebClient section of the Spring Framework reference * documentation for more details and example code. * * @author Arjen Poutsma * @author Brian Clozel * @author Roy Clarkson * @author Juergen Hoeller * @since 3.0 * @see HttpMessageConverter * @see RequestCallback * @see ResponseExtractor * @see ResponseErrorHandler */
public class RestTemplate extends InterceptingHttpAccessor implements RestOperations { private static boolean romePresent; private static final boolean jaxb2Present; private static final boolean jackson2Present; private static final boolean jackson2XmlPresent; private static final boolean jackson2SmilePresent; private static final boolean jackson2CborPresent; private static final boolean gsonPresent; private static final boolean jsonbPresent; static { ClassLoader classLoader = RestTemplate.class.getClassLoader(); romePresent = ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", classLoader); jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", classLoader); jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) && ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader); jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader); jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader); jackson2CborPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory", classLoader); gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader); jsonbPresent = ClassUtils.isPresent("javax.json.bind.Jsonb", classLoader); } private final List<HttpMessageConverter<?>> messageConverters = new ArrayList<>(); private ResponseErrorHandler errorHandler = new DefaultResponseErrorHandler(); private UriTemplateHandler uriTemplateHandler; private final ResponseExtractor<HttpHeaders> headersExtractor = new HeadersExtractor();
Create a new instance of the RestTemplate using default settings. Default HttpMessageConverters are initialized.
/** * Create a new instance of the {@link RestTemplate} using default settings. * Default {@link HttpMessageConverter HttpMessageConverters} are initialized. */
public RestTemplate() { this.messageConverters.add(new ByteArrayHttpMessageConverter()); this.messageConverters.add(new StringHttpMessageConverter()); this.messageConverters.add(new ResourceHttpMessageConverter(false)); try { this.messageConverters.add(new SourceHttpMessageConverter<>()); } catch (Error err) { // Ignore when no TransformerFactory implementation is available } 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()); } else if (jsonbPresent) { this.messageConverters.add(new JsonbHttpMessageConverter()); } if (jackson2SmilePresent) { this.messageConverters.add(new MappingJackson2SmileHttpMessageConverter()); } if (jackson2CborPresent) { this.messageConverters.add(new MappingJackson2CborHttpMessageConverter()); } this.uriTemplateHandler = initUriTemplateHandler(); }
Create a new instance of the RestTemplate based on the given ClientHttpRequestFactory.
Params:
  • requestFactory – the HTTP request factory to use
See Also:
/** * Create a new instance of the {@link RestTemplate} based on the given {@link ClientHttpRequestFactory}. * @param requestFactory the 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 RestTemplate using the given list of HttpMessageConverter to use.
Params:
Since:3.2.7
/** * 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<HttpMessageConverter<?>> messageConverters) { Assert.notEmpty(messageConverters, "At least one HttpMessageConverter required"); this.messageConverters.addAll(messageConverters); this.uriTemplateHandler = initUriTemplateHandler(); } private static DefaultUriBuilderFactory initUriTemplateHandler() { DefaultUriBuilderFactory uriFactory = new DefaultUriBuilderFactory(); uriFactory.setEncodingMode(EncodingMode.URI_COMPONENT); // for backwards compatibility.. return uriFactory; }
Set the message body converters to use.

These converters are used to convert from and to HTTP requests and responses.

/** * Set the message body converters to use. * <p>These converters are used to convert from and to HTTP requests and responses. */
public void setMessageConverters(List<HttpMessageConverter<?>> 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 list of message body converters.

The returned List is active and may get appended to.

/** * Return the list of message body converters. * <p>The returned {@link List} is active and may get appended to. */
public List<HttpMessageConverter<?>> getMessageConverters() { return this.messageConverters; }
Set the error handler.

By default, RestTemplate uses a DefaultResponseErrorHandler.

/** * Set the error handler. * <p>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.
/** * Return the error handler. */
public ResponseErrorHandler getErrorHandler() { return this.errorHandler; }
Configure default URI variable values. This is a shortcut for:
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory();
handler.setDefaultUriVariables(...);
RestTemplate restTemplate = new RestTemplate();
restTemplate.setUriTemplateHandler(handler);
Params:
  • uriVars – the default URI variable values
Since:4.3
/** * Configure default URI variable values. This is a shortcut for: * <pre class="code"> * DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(); * handler.setDefaultUriVariables(...); * * RestTemplate restTemplate = new RestTemplate(); * restTemplate.setUriTemplateHandler(handler); * </pre> * @param uriVars the default URI variable values * @since 4.3 */
@SuppressWarnings("deprecation") public void setDefaultUriVariables(Map<String, ?> uriVars) { if (this.uriTemplateHandler instanceof DefaultUriBuilderFactory) { ((DefaultUriBuilderFactory) this.uriTemplateHandler).setDefaultUriVariables(uriVars); } else if (this.uriTemplateHandler instanceof org.springframework.web.util.AbstractUriTemplateHandler) { ((org.springframework.web.util.AbstractUriTemplateHandler) this.uriTemplateHandler) .setDefaultUriVariables(uriVars); } else { throw new IllegalArgumentException( "This property is not supported with the configured UriTemplateHandler."); } }
Configure a strategy for expanding URI templates.

By default, DefaultUriBuilderFactory is used and for backwards compatibility, the encoding mode is set to URI_COMPONENT. As of 5.0.8, prefer using TEMPLATE_AND_VALUES.

Note: in 5.0 the switch from DefaultUriTemplateHandler (deprecated in 4.3), as the default to use, to DefaultUriBuilderFactory brings in a different default for the parsePath property (switching from false to true).

Params:
  • handler – the URI template handler to use
/** * Configure a strategy for expanding URI templates. * <p>By default, {@link DefaultUriBuilderFactory} is used and for * backwards compatibility, the encoding mode is set to * {@link EncodingMode#URI_COMPONENT URI_COMPONENT}. As of 5.0.8, prefer * using {@link EncodingMode#TEMPLATE_AND_VALUES TEMPLATE_AND_VALUES}. * <p><strong>Note:</strong> in 5.0 the switch from * {@link org.springframework.web.util.DefaultUriTemplateHandler * DefaultUriTemplateHandler} (deprecated in 4.3), as the default to use, to * {@link DefaultUriBuilderFactory} brings in a different default for the * {@code parsePath} property (switching from false to true). * @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.
/** * Return the configured URI template handler. */
public UriTemplateHandler getUriTemplateHandler() { return this.uriTemplateHandler; } // GET @Override @Nullable public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException { RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); HttpMessageConverterExtractor<T> responseExtractor = new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger); return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables); } @Override @Nullable public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException { RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); HttpMessageConverterExtractor<T> responseExtractor = new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger); return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables); } @Override @Nullable public <T> T getForObject(URI url, Class<T> responseType) throws RestClientException { RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); HttpMessageConverterExtractor<T> responseExtractor = new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger); return execute(url, HttpMethod.GET, requestCallback, responseExtractor); } @Override public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables) throws RestClientException { RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType); return nonNull(execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables)); } @Override public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException { RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType); return nonNull(execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables)); } @Override public <T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType) throws RestClientException { RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType); return nonNull(execute(url, HttpMethod.GET, requestCallback, responseExtractor)); } // HEAD @Override public HttpHeaders headForHeaders(String url, Object... uriVariables) throws RestClientException { return nonNull(execute(url, HttpMethod.HEAD, null, headersExtractor(), uriVariables)); } @Override public HttpHeaders headForHeaders(String url, Map<String, ?> uriVariables) throws RestClientException { return nonNull(execute(url, HttpMethod.HEAD, null, headersExtractor(), uriVariables)); } @Override public HttpHeaders headForHeaders(URI url) throws RestClientException { return nonNull(execute(url, HttpMethod.HEAD, null, headersExtractor())); } // POST @Override @Nullable public URI postForLocation(String url, @Nullable Object request, Object... uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request); HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, headersExtractor(), uriVariables); return (headers != null ? headers.getLocation() : null); } @Override @Nullable public URI postForLocation(String url, @Nullable Object request, Map<String, ?> uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request); HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, headersExtractor(), uriVariables); return (headers != null ? headers.getLocation() : null); } @Override @Nullable public URI postForLocation(URI url, @Nullable Object request) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request); HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, headersExtractor()); return (headers != null ? headers.getLocation() : null); } @Override @Nullable public <T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Object... uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request, responseType); HttpMessageConverterExtractor<T> responseExtractor = new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger); return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables); } @Override @Nullable public <T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request, responseType); HttpMessageConverterExtractor<T> responseExtractor = new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger); return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables); } @Override @Nullable public <T> T postForObject(URI url, @Nullable Object request, Class<T> responseType) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request, responseType); HttpMessageConverterExtractor<T> responseExtractor = new HttpMessageConverterExtractor<>(responseType, getMessageConverters()); return execute(url, HttpMethod.POST, requestCallback, responseExtractor); } @Override public <T> ResponseEntity<T> postForEntity(String url, @Nullable Object request, Class<T> responseType, Object... uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request, responseType); ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType); return nonNull(execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables)); } @Override public <T> ResponseEntity<T> postForEntity(String url, @Nullable Object request, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request, responseType); ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType); return nonNull(execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables)); } @Override public <T> ResponseEntity<T> postForEntity(URI url, @Nullable Object request, Class<T> responseType) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request, responseType); ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType); return nonNull(execute(url, HttpMethod.POST, requestCallback, responseExtractor)); } // PUT @Override public void put(String url, @Nullable Object request, Object... uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request); execute(url, HttpMethod.PUT, requestCallback, null, uriVariables); } @Override public void put(String url, @Nullable Object request, Map<String, ?> uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request); execute(url, HttpMethod.PUT, requestCallback, null, uriVariables); } @Override public void put(URI url, @Nullable Object request) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request); execute(url, HttpMethod.PUT, requestCallback, null); } // PATCH @Override @Nullable public <T> T patchForObject(String url, @Nullable Object request, Class<T> responseType, Object... uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request, responseType); HttpMessageConverterExtractor<T> responseExtractor = new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger); return execute(url, HttpMethod.PATCH, requestCallback, responseExtractor, uriVariables); } @Override @Nullable public <T> T patchForObject(String url, @Nullable Object request, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request, responseType); HttpMessageConverterExtractor<T> responseExtractor = new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger); return execute(url, HttpMethod.PATCH, requestCallback, responseExtractor, uriVariables); } @Override @Nullable public <T> T patchForObject(URI url, @Nullable Object request, Class<T> responseType) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request, responseType); HttpMessageConverterExtractor<T> 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<String, ?> 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<HttpMethod> optionsForAllow(String url, Object... uriVariables) throws RestClientException { ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor(); HttpHeaders headers = execute(url, HttpMethod.OPTIONS, null, headersExtractor, uriVariables); return (headers != null ? headers.getAllow() : Collections.emptySet()); } @Override public Set<HttpMethod> optionsForAllow(String url, Map<String, ?> uriVariables) throws RestClientException { ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor(); HttpHeaders headers = execute(url, HttpMethod.OPTIONS, null, headersExtractor, uriVariables); return (headers != null ? headers.getAllow() : Collections.emptySet()); } @Override public Set<HttpMethod> optionsForAllow(URI url) throws RestClientException { ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor(); HttpHeaders headers = execute(url, HttpMethod.OPTIONS, null, headersExtractor); return (headers != null ? headers.getAllow() : Collections.emptySet()); } // exchange @Override public <T> ResponseEntity<T> exchange(String url, HttpMethod method, @Nullable HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType); ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType); return nonNull(execute(url, method, requestCallback, responseExtractor, uriVariables)); } @Override public <T> ResponseEntity<T> exchange(String url, HttpMethod method, @Nullable HttpEntity<?> requestEntity, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType); ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType); return nonNull(execute(url, method, requestCallback, responseExtractor, uriVariables)); } @Override public <T> ResponseEntity<T> exchange(URI url, HttpMethod method, @Nullable HttpEntity<?> requestEntity, Class<T> responseType) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType); ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType); return nonNull(execute(url, method, requestCallback, responseExtractor)); } @Override public <T> ResponseEntity<T> exchange(String url, HttpMethod method, @Nullable HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType, Object... uriVariables) throws RestClientException { Type type = responseType.getType(); RequestCallback requestCallback = httpEntityCallback(requestEntity, type); ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(type); return nonNull(execute(url, method, requestCallback, responseExtractor, uriVariables)); } @Override public <T> ResponseEntity<T> exchange(String url, HttpMethod method, @Nullable HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType, Map<String, ?> uriVariables) throws RestClientException { Type type = responseType.getType(); RequestCallback requestCallback = httpEntityCallback(requestEntity, type); ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(type); return nonNull(execute(url, method, requestCallback, responseExtractor, uriVariables)); } @Override public <T> ResponseEntity<T> exchange(URI url, HttpMethod method, @Nullable HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType) throws RestClientException { Type type = responseType.getType(); RequestCallback requestCallback = httpEntityCallback(requestEntity, type); ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(type); return nonNull(execute(url, method, requestCallback, responseExtractor)); } @Override public <T> ResponseEntity<T> exchange(RequestEntity<?> requestEntity, Class<T> responseType) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType); ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType); return nonNull(doExecute(requestEntity.getUrl(), requestEntity.getMethod(), requestCallback, responseExtractor)); } @Override public <T> ResponseEntity<T> exchange(RequestEntity<?> requestEntity, ParameterizedTypeReference<T> responseType) throws RestClientException { Type type = responseType.getType(); RequestCallback requestCallback = httpEntityCallback(requestEntity, type); ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(type); return nonNull(doExecute(requestEntity.getUrl(), requestEntity.getMethod(), requestCallback, responseExtractor)); } // General execution
{@inheritDoc}

To provide a RequestCallback or ResponseExtractor only, but not both, consider using:

/** * {@inheritDoc} * <p>To provide a {@code RequestCallback} or {@code ResponseExtractor} only, * but not both, consider using: * <ul> * <li>{@link #acceptHeaderRequestCallback(Class)} * <li>{@link #httpEntityCallback(Object)} * <li>{@link #httpEntityCallback(Object, Type)} * <li>{@link #responseEntityExtractor(Type)} * </ul> */
@Override @Nullable public <T> T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor, Object... uriVariables) throws RestClientException { URI expanded = getUriTemplateHandler().expand(url, uriVariables); return doExecute(expanded, method, requestCallback, responseExtractor); }
{@inheritDoc}

To provide a RequestCallback or ResponseExtractor only, but not both, consider using:

/** * {@inheritDoc} * <p>To provide a {@code RequestCallback} or {@code ResponseExtractor} only, * but not both, consider using: * <ul> * <li>{@link #acceptHeaderRequestCallback(Class)} * <li>{@link #httpEntityCallback(Object)} * <li>{@link #httpEntityCallback(Object, Type)} * <li>{@link #responseEntityExtractor(Type)} * </ul> */
@Override @Nullable public <T> T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor, Map<String, ?> uriVariables) throws RestClientException { URI expanded = getUriTemplateHandler().expand(url, uriVariables); return doExecute(expanded, method, requestCallback, responseExtractor); }
{@inheritDoc}

To provide a RequestCallback or ResponseExtractor only, but not both, consider using:

/** * {@inheritDoc} * <p>To provide a {@code RequestCallback} or {@code ResponseExtractor} only, * but not both, consider using: * <ul> * <li>{@link #acceptHeaderRequestCallback(Class)} * <li>{@link #httpEntityCallback(Object)} * <li>{@link #httpEntityCallback(Object, Type)} * <li>{@link #responseEntityExtractor(Type)} * </ul> */
@Override @Nullable public <T> T execute(URI url, HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException { return doExecute(url, method, requestCallback, responseExtractor); }
Execute the given method on the provided URI.

The ClientHttpRequest is processed using the RequestCallback; the response with the ResponseExtractor.

Params:
  • url – the fully-expanded URL to connect to
  • method – the HTTP method to execute (GET, POST, etc.)
  • requestCallback – object that prepares the request (can be null)
  • responseExtractor – object that extracts the return value from the response (can be null)
Returns:an arbitrary object, as returned by the ResponseExtractor
/** * Execute the given method on the provided URI. * <p>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} */
@Nullable protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException { Assert.notNull(url, "URI is required"); Assert.notNull(method, "HttpMethod is required"); ClientHttpResponse response = null; try { ClientHttpRequest request = createRequest(url, method); if (requestCallback != null) { requestCallback.doWithRequest(request); } response = request.execute(); handleResponse(url, method, response); return (responseExtractor != null ? responseExtractor.extractData(response) : null); } catch (IOException ex) { String resource = url.toString(); String query = url.getRawQuery(); resource = (query != null ? resource.substring(0, resource.indexOf('?')) : 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 ResponseErrorHandler if necessary.

Can be overridden in subclasses.

Params:
  • url – the fully-expanded URL to connect to
  • method – the HTTP method to execute (GET, POST, etc.)
  • response – the resulting ClientHttpResponse
Throws:
See Also:
Since:4.1.6
/** * Handle the given response, performing appropriate logging and * invoking the {@link ResponseErrorHandler} if necessary. * <p>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 { int code = response.getRawStatusCode(); HttpStatus status = HttpStatus.resolve(code); logger.debug("Response " + (status != null ? status : code)); } catch (IOException ex) { // ignore } } if (hasError) { errorHandler.handleError(url, method, response); } }
Return a RequestCallback that sets the request Accept header based on the given response type, cross-checked against the configured message converters.
/** * Return a {@code RequestCallback} that sets the request {@code Accept} * header based on the given response type, cross-checked against the * configured message converters. */
public <T> RequestCallback acceptHeaderRequestCallback(Class<T> responseType) { return new AcceptHeaderRequestCallback(responseType); }
Return a RequestCallback implementation that writes the given object to the request stream.
/** * Return a {@code RequestCallback} implementation that writes the given * object to the request stream. */
public <T> RequestCallback httpEntityCallback(@Nullable Object requestBody) { return new HttpEntityRequestCallback(requestBody); }
Return a RequestCallback implementation that:
  1. Sets the request Accept header based on the given response type, cross-checked against the configured message converters.
  2. Writes the given object to the request stream.
/** * Return a {@code RequestCallback} implementation that: * <ol> * <li>Sets the request {@code Accept} header based on the given response * type, cross-checked against the configured message converters. * <li>Writes the given object to the request stream. * </ol> */
public <T> RequestCallback httpEntityCallback(@Nullable Object requestBody, Type responseType) { return new HttpEntityRequestCallback(requestBody, responseType); }
Return a ResponseExtractor that prepares a ResponseEntity.
/** * Return a {@code ResponseExtractor} that prepares a {@link ResponseEntity}. */
public <T> ResponseExtractor<ResponseEntity<T>> responseEntityExtractor(Type responseType) { return new ResponseEntityResponseExtractor<>(responseType); }
Return a response extractor for HttpHeaders.
/** * Return a response extractor for {@link HttpHeaders}. */
protected ResponseExtractor<HttpHeaders> headersExtractor() { return this.headersExtractor; } private static <T> T nonNull(@Nullable T result) { Assert.state(result != null, "No result"); return result; }
Request callback implementation that prepares the request's accept headers.
/** * Request callback implementation that prepares the request's accept headers. */
private class AcceptHeaderRequestCallback implements RequestCallback { @Nullable private final Type responseType; public AcceptHeaderRequestCallback(@Nullable Type responseType) { this.responseType = responseType; } @Override public void doWithRequest(ClientHttpRequest request) throws IOException { if (this.responseType != null) { List<MediaType> allSupportedMediaTypes = getMessageConverters().stream() .filter(converter -> canReadResponse(this.responseType, converter)) .flatMap(this::getSupportedMediaTypes) .distinct() .sorted(MediaType.SPECIFICITY_COMPARATOR) .collect(Collectors.toList()); if (logger.isDebugEnabled()) { logger.debug("Accept=" + allSupportedMediaTypes); } request.getHeaders().setAccept(allSupportedMediaTypes); } } private boolean canReadResponse(Type responseType, HttpMessageConverter<?> converter) { Class<?> responseClass = (responseType instanceof Class ? (Class<?>) responseType : null); if (responseClass != null) { return converter.canRead(responseClass, null); } else if (converter instanceof GenericHttpMessageConverter) { GenericHttpMessageConverter<?> genericConverter = (GenericHttpMessageConverter<?>) converter; return genericConverter.canRead(responseType, null, null); } return false; } private Stream<MediaType> getSupportedMediaTypes(HttpMessageConverter<?> messageConverter) { return messageConverter.getSupportedMediaTypes() .stream() .map(mediaType -> { if (mediaType.getCharset() != null) { return new MediaType(mediaType.getType(), mediaType.getSubtype()); } return mediaType; }); } }
Request callback implementation that writes the given object to the request stream.
/** * Request callback implementation that writes the given object to the request stream. */
private class HttpEntityRequestCallback extends AcceptHeaderRequestCallback { private final HttpEntity<?> requestEntity; public HttpEntityRequestCallback(@Nullable Object requestBody) { this(requestBody, null); } public HttpEntityRequestCallback(@Nullable Object requestBody, @Nullable 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); Object requestBody = this.requestEntity.getBody(); if (requestBody == null) { HttpHeaders httpHeaders = httpRequest.getHeaders(); HttpHeaders requestHeaders = this.requestEntity.getHeaders(); if (!requestHeaders.isEmpty()) { requestHeaders.forEach((key, values) -> httpHeaders.put(key, new LinkedList<>(values))); } if (httpHeaders.getContentLength() < 0) { httpHeaders.setContentLength(0L); } } else { Class<?> requestBodyClass = requestBody.getClass(); Type requestBodyType = (this.requestEntity instanceof RequestEntity ? ((RequestEntity<?>)this.requestEntity).getType() : requestBodyClass); HttpHeaders httpHeaders = httpRequest.getHeaders(); HttpHeaders requestHeaders = this.requestEntity.getHeaders(); MediaType requestContentType = requestHeaders.getContentType(); for (HttpMessageConverter<?> messageConverter : getMessageConverters()) { if (messageConverter instanceof GenericHttpMessageConverter) { GenericHttpMessageConverter<Object> genericConverter = (GenericHttpMessageConverter<Object>) messageConverter; if (genericConverter.canWrite(requestBodyType, requestBodyClass, requestContentType)) { if (!requestHeaders.isEmpty()) { requestHeaders.forEach((key, values) -> httpHeaders.put(key, new LinkedList<>(values))); } logBody(requestBody, requestContentType, genericConverter); genericConverter.write(requestBody, requestBodyType, requestContentType, httpRequest); return; } } else if (messageConverter.canWrite(requestBodyClass, requestContentType)) { if (!requestHeaders.isEmpty()) { requestHeaders.forEach((key, values) -> httpHeaders.put(key, new LinkedList<>(values))); } logBody(requestBody, requestContentType, messageConverter); ((HttpMessageConverter<Object>) messageConverter).write( requestBody, requestContentType, httpRequest); return; } } String message = "No HttpMessageConverter for [" + requestBodyClass.getName() + "]"; if (requestContentType != null) { message += " and content type [" + requestContentType + "]"; } throw new RestClientException(message); } } private void logBody(Object body, @Nullable MediaType mediaType, HttpMessageConverter<?> converter) { if (logger.isDebugEnabled()) { if (mediaType != null) { logger.debug("Writing [" + body + "] as \"" + mediaType + "\""); } else { String classname = converter.getClass().getName(); logger.debug("Writing [" + body + "] with " + classname); } } } }
Response extractor for HttpEntity.
/** * Response extractor for {@link HttpEntity}. */
private class ResponseEntityResponseExtractor<T> implements ResponseExtractor<ResponseEntity<T>> { @Nullable private final HttpMessageConverterExtractor<T> delegate; public ResponseEntityResponseExtractor(@Nullable Type responseType) { if (responseType != null && Void.class != responseType) { this.delegate = new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger); } else { this.delegate = null; } } @Override public ResponseEntity<T> extractData(ClientHttpResponse response) throws IOException { if (this.delegate != null) { T body = this.delegate.extractData(response); return ResponseEntity.status(response.getRawStatusCode()).headers(response.getHeaders()).body(body); } else { return ResponseEntity.status(response.getRawStatusCode()).headers(response.getHeaders()).build(); } } }
Response extractor that extracts the response HttpHeaders.
/** * Response extractor that extracts the response {@link HttpHeaders}. */
private static class HeadersExtractor implements ResponseExtractor<HttpHeaders> { @Override public HttpHeaders extractData(ClientHttpResponse response) { return response.getHeaders(); } } }