/*
* Copyright 2012-2017 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.boot.test.web.client;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.config.RequestConfig.Builder;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.protocol.HttpContext;
import org.apache.http.ssl.SSLContextBuilder;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.boot.web.client.RootUriTemplateHandler;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.support.BasicAuthorizationInterceptor;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RequestCallback;
import org.springframework.web.client.ResponseExtractor;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.DefaultUriBuilderFactory;
import org.springframework.web.util.UriTemplateHandler;
/**
* Convenient alternative of {@link RestTemplate} that is suitable for integration tests.
* They are fault tolerant, and optionally can carry Basic authentication headers. If
* Apache Http Client 4.3.2 or better is available (recommended) it will be used as the
* client, and by default configured to ignore cookies and redirects.
* <p>
* Note: To prevent injection problems this class internally does not extend
* {@link RestTemplate}. If you need access to the underlying {@link RestTemplate} use
* {@link #getRestTemplate()}.
* <p>
* If you are using the
* {@link org.springframework.boot.test.context.SpringBootTest @SpringBootTest}
* annotation, a {@link TestRestTemplate} is automatically available and can be
* {@code @Autowired} into your test. If you need customizations (for example to adding
* additional message converters) use a {@link RestTemplateBuilder} {@code @Bean}.
*
* @author Dave Syer
* @author Phillip Webb
* @author Andy Wilkinson
* @since 1.4.0
*/
public class TestRestTemplate {
private final RestTemplate restTemplate;
private final HttpClientOption[] httpClientOptions;
/**
* Create a new {@link TestRestTemplate} instance.
* @param restTemplateBuilder builder used to configure underlying
* {@link RestTemplate}
* @since 1.4.1
*/
public TestRestTemplate(RestTemplateBuilder restTemplateBuilder) {
this(buildRestTemplate(restTemplateBuilder));
}
/**
* Create a new {@link TestRestTemplate} instance.
* @param httpClientOptions client options to use if the Apache HTTP Client is used
*/
public TestRestTemplate(HttpClientOption... httpClientOptions) {
this(null, null, httpClientOptions);
}
/**
* Create a new {@link TestRestTemplate} instance with the specified credentials.
* @param username the username to use (or {@code null})
* @param password the password (or {@code null})
* @param httpClientOptions client options to use if the Apache HTTP Client is used
*/
public TestRestTemplate(String username, String password,
HttpClientOption... httpClientOptions) {
this(new RestTemplate(), username, password, httpClientOptions);
}
public TestRestTemplate(RestTemplate restTemplate) {
this(restTemplate, null, null);
}
public TestRestTemplate(RestTemplate restTemplate, String username, String password,
HttpClientOption... httpClientOptions) {
Assert.notNull(restTemplate, "RestTemplate must not be null");
this.httpClientOptions = httpClientOptions;
if (ClassUtils.isPresent("org.apache.http.client.config.RequestConfig", null)) {
restTemplate.setRequestFactory(
new CustomHttpComponentsClientHttpRequestFactory(httpClientOptions));
}
addAuthentication(restTemplate, username, password);
restTemplate.setErrorHandler(new NoOpResponseErrorHandler());
this.restTemplate = restTemplate;
}
private static RestTemplate buildRestTemplate(
RestTemplateBuilder restTemplateBuilder) {
Assert.notNull(restTemplateBuilder, "RestTemplateBuilder must not be null");
return restTemplateBuilder.build();
}
private void addAuthentication(RestTemplate restTemplate, String username,
String password) {
if (username == null) {
return;
}
List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
if (interceptors == null) {
interceptors = Collections.emptyList();
}
interceptors = new ArrayList<>(interceptors);
Iterator<ClientHttpRequestInterceptor> iterator = interceptors.iterator();
while (iterator.hasNext()) {
if (iterator.next() instanceof BasicAuthorizationInterceptor) {
iterator.remove();
}
}
interceptors.add(new BasicAuthorizationInterceptor(username, password));
restTemplate.setInterceptors(interceptors);
}
/**
* Configure the {@link UriTemplateHandler} to use to expand URI templates. By default
* the {@link DefaultUriBuilderFactory} 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) {
this.restTemplate.setUriTemplateHandler(handler);
}
/**
* Retrieve a representation by doing a GET on the specified URL. The response (if
* any) is converted and returned.
* <p>
* URI Template variables are expanded using the given URI variables, if any.
* @param url the URL
* @param responseType the type of the return value
* @param urlVariables the variables to expand the template
* @param <T> the type of the return value
* @return the converted object
* @throws RestClientException on client-side HTTP error on client-side HTTP error
* @see RestTemplate#getForObject(String, Class, Object...)
*/
public <T> T getForObject(String url, Class<T> responseType, Object... urlVariables)
throws RestClientException {
return this.restTemplate.getForObject(url, responseType, urlVariables);
}
/**
* Retrieve a representation by doing a GET on the URI template. The response (if any)
* is converted and returned.
* <p>
* URI Template variables are expanded using the given map.
* @param url the URL
* @param responseType the type of the return value
* @param urlVariables the map containing variables for the URI template
* @param <T> the type of the return value
* @return the converted object
* @throws RestClientException on client-side HTTP error
* @see RestTemplate#getForObject(String, Class, Object...)
*/
public <T> T getForObject(String url, Class<T> responseType,
Map<String, ?> urlVariables) throws RestClientException {
return this.restTemplate.getForObject(url, responseType, urlVariables);
}
/**
* Retrieve a representation by doing a GET on the URL . The response (if any) is
* converted and returned.
* @param url the URL
* @param responseType the type of the return value
* @param <T> the type of the return value
* @return the converted object
* @throws RestClientException on client-side HTTP error
* @see RestTemplate#getForObject(java.net.URI, java.lang.Class)
*/
public <T> T getForObject(URI url, Class<T> responseType) throws RestClientException {
return this.restTemplate.getForObject(applyRootUriIfNecessary(url), responseType);
}
/**
* Retrieve an entity by doing a GET on the specified URL. The response is converted
* and stored in an {@link ResponseEntity}.
* <p>
* URI Template variables are expanded using the given URI variables, if any.
* @param url the URL
* @param responseType the type of the return value
* @param urlVariables the variables to expand the template
* @param <T> the type of the return value
* @return the entity
* @throws RestClientException on client-side HTTP error
* @see RestTemplate#getForEntity(java.lang.String, java.lang.Class,
* java.lang.Object[])
*/
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType,
Object... urlVariables) throws RestClientException {
return this.restTemplate.getForEntity(url, responseType, urlVariables);
}
/**
* Retrieve a representation by doing a GET on the URI template. The response is
* converted and stored in an {@link ResponseEntity}.
* <p>
* URI Template variables are expanded using the given map.
* @param url the URL
* @param responseType the type of the return value
* @param urlVariables the map containing variables for the URI template
* @param <T> the type of the return value
* @return the converted object
* @throws RestClientException on client-side HTTP error
* @see RestTemplate#getForEntity(java.lang.String, java.lang.Class, java.util.Map)
*/
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType,
Map<String, ?> urlVariables) throws RestClientException {
return this.restTemplate.getForEntity(url, responseType, urlVariables);
}
/**
* Retrieve a representation by doing a GET on the URL . The response is converted and
* stored in an {@link ResponseEntity}.
* @param url the URL
* @param responseType the type of the return value
* @param <T> the type of the return value
* @return the converted object
* @throws RestClientException on client-side HTTP error
* @see RestTemplate#getForEntity(java.net.URI, java.lang.Class)
*/
public <T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType)
throws RestClientException {
return this.restTemplate.getForEntity(applyRootUriIfNecessary(url), responseType);
}
/**
* Retrieve all headers of the resource specified by the URI template.
* <p>
* URI Template variables are expanded using the given URI variables, if any.
* @param url the URL
* @param urlVariables the variables to expand the template
* @return all HTTP headers of that resource
* @throws RestClientException on client-side HTTP error
* @see RestTemplate#headForHeaders(java.lang.String, java.lang.Object[])
*/
public HttpHeaders headForHeaders(String url, Object... urlVariables)
throws RestClientException {
return this.restTemplate.headForHeaders(url, urlVariables);
}
/**
* Retrieve all headers of the resource specified by the URI template.
* <p>
* URI Template variables are expanded using the given map.
* @param url the URL
* @param urlVariables the map containing variables for the URI template
* @return all HTTP headers of that resource
* @throws RestClientException on client-side HTTP error
* @see RestTemplate#headForHeaders(java.lang.String, java.util.Map)
*/
public HttpHeaders headForHeaders(String url, Map<String, ?> urlVariables)
throws RestClientException {
return this.restTemplate.headForHeaders(url, urlVariables);
}
/**
* Retrieve all headers of the resource specified by the URL.
* @param url the URL
* @return all HTTP headers of that resource
* @throws RestClientException on client-side HTTP error
* @see RestTemplate#headForHeaders(java.net.URI)
*/
public HttpHeaders headForHeaders(URI url) throws RestClientException {
return this.restTemplate.headForHeaders(applyRootUriIfNecessary(url));
}
/**
* Create a new resource by POSTing the given object to the URI template, and returns
* the value of the {@code Location} header. This header typically indicates where the
* new resource is stored.
* <p>
* URI Template variables are expanded using the given URI variables, if any.
* <p>
* The {@code request} parameter can be a {@link HttpEntity} in order to add
* additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be POSTed, may be {@code null}
* @param urlVariables the variables to expand the template
* @return the value for the {@code Location} header
* @throws RestClientException on client-side HTTP error
* @see HttpEntity
* @see RestTemplate#postForLocation(java.lang.String, java.lang.Object,
* java.lang.Object[])
*/
public URI postForLocation(String url, Object request, Object... urlVariables)
throws RestClientException {
return this.restTemplate.postForLocation(url, request, urlVariables);
}
/**
* Create a new resource by POSTing the given object to the URI template, and returns
* the value of the {@code Location} header. This header typically indicates where the
* new resource is stored.
* <p>
* URI Template variables are expanded using the given map.
* <p>
* The {@code request} parameter can be a {@link HttpEntity} in order to add
* additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be POSTed, may be {@code null}
* @param urlVariables the variables to expand the template
* @return the value for the {@code Location} header
* @throws RestClientException on client-side HTTP error
* @see HttpEntity
* @see RestTemplate#postForLocation(java.lang.String, java.lang.Object,
* java.util.Map)
*/
public URI postForLocation(String url, Object request, Map<String, ?> urlVariables)
throws RestClientException {
return this.restTemplate.postForLocation(url, request, urlVariables);
}
/**
* Create a new resource by POSTing the given object to the URL, and returns the value
* of the {@code Location} header. This header typically indicates where the new
* resource is stored.
* <p>
* The {@code request} parameter can be a {@link HttpEntity} in order to add
* additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be POSTed, may be {@code null}
* @return the value for the {@code Location} header
* @throws RestClientException on client-side HTTP error
* @see HttpEntity
* @see RestTemplate#postForLocation(java.net.URI, java.lang.Object)
*/
public URI postForLocation(URI url, Object request) throws RestClientException {
return this.restTemplate.postForLocation(applyRootUriIfNecessary(url), request);
}
/**
* Create a new resource by POSTing the given object to the URI template, and returns
* the representation found in the response.
* <p>
* URI Template variables are expanded using the given URI variables, if any.
* <p>
* The {@code request} parameter can be a {@link HttpEntity} in order to add
* additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be POSTed, may be {@code null}
* @param responseType the type of the return value
* @param urlVariables the variables to expand the template
* @param <T> the type of the return value
* @return the converted object
* @throws RestClientException on client-side HTTP error
* @see HttpEntity
* @see RestTemplate#postForObject(java.lang.String, java.lang.Object,
* java.lang.Class, java.lang.Object[])
*/
public <T> T postForObject(String url, Object request, Class<T> responseType,
Object... urlVariables) throws RestClientException {
return this.restTemplate.postForObject(url, request, responseType, urlVariables);
}
/**
* Create a new resource by POSTing the given object to the URI template, and returns
* the representation found in the response.
* <p>
* URI Template variables are expanded using the given map.
* <p>
* The {@code request} parameter can be a {@link HttpEntity} in order to add
* additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be POSTed, may be {@code null}
* @param responseType the type of the return value
* @param urlVariables the variables to expand the template
* @param <T> the type of the return value
* @return the converted object
* @throws RestClientException on client-side HTTP error
* @see HttpEntity
* @see RestTemplate#postForObject(java.lang.String, java.lang.Object,
* java.lang.Class, java.util.Map)
*/
public <T> T postForObject(String url, Object request, Class<T> responseType,
Map<String, ?> urlVariables) throws RestClientException {
return this.restTemplate.postForObject(url, request, responseType, urlVariables);
}
/**
* Create a new resource by POSTing the given object to the URL, and returns the
* representation found in the response.
* <p>
* The {@code request} parameter can be a {@link HttpEntity} in order to add
* additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be POSTed, may be {@code null}
* @param responseType the type of the return value
* @param <T> the type of the return value
* @return the converted object
* @throws RestClientException on client-side HTTP error
* @see HttpEntity
* @see RestTemplate#postForObject(java.net.URI, java.lang.Object, java.lang.Class)
*/
public <T> T postForObject(URI url, Object request, Class<T> responseType)
throws RestClientException {
return this.restTemplate.postForObject(applyRootUriIfNecessary(url), request,
responseType);
}
/**
* Create a new resource by POSTing the given object to the URI template, and returns
* the response as {@link ResponseEntity}.
* <p>
* URI Template variables are expanded using the given URI variables, if any.
* <p>
* The {@code request} parameter can be a {@link HttpEntity} in order to add
* additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be POSTed, may be {@code null}
* @param responseType the response type to return
* @param urlVariables the variables to expand the template
* @param <T> the type of the return value
* @return the converted object
* @throws RestClientException on client-side HTTP error
* @see HttpEntity
* @see RestTemplate#postForEntity(java.lang.String, java.lang.Object,
* java.lang.Class, java.lang.Object[])
*/
public <T> ResponseEntity<T> postForEntity(String url, Object request,
Class<T> responseType, Object... urlVariables) throws RestClientException {
return this.restTemplate.postForEntity(url, request, responseType, urlVariables);
}
/**
* Create a new resource by POSTing the given object to the URI template, and returns
* the response as {@link HttpEntity}.
* <p>
* URI Template variables are expanded using the given map.
* <p>
* The {@code request} parameter can be a {@link HttpEntity} in order to add
* additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be POSTed, may be {@code null}
* @param responseType the response type to return
* @param urlVariables the variables to expand the template
* @param <T> the type of the return value
* @return the converted object
* @throws RestClientException on client-side HTTP error
* @see HttpEntity
* @see RestTemplate#postForEntity(java.lang.String, java.lang.Object,
* java.lang.Class, java.util.Map)
*/
public <T> ResponseEntity<T> postForEntity(String url, Object request,
Class<T> responseType, Map<String, ?> urlVariables)
throws RestClientException {
return this.restTemplate.postForEntity(url, request, responseType, urlVariables);
}
/**
* Create a new resource by POSTing the given object to the URL, and returns the
* response as {@link ResponseEntity}.
* <p>
* The {@code request} parameter can be a {@link HttpEntity} in order to add
* additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be POSTed, may be {@code null}
* @param responseType the response type to return
* @param <T> the type of the return value
* @return the converted object
* @throws RestClientException on client-side HTTP error
* @see HttpEntity
* @see RestTemplate#postForEntity(java.net.URI, java.lang.Object, java.lang.Class)
*/
public <T> ResponseEntity<T> postForEntity(URI url, Object request,
Class<T> responseType) throws RestClientException {
return this.restTemplate.postForEntity(applyRootUriIfNecessary(url), request,
responseType);
}
/**
* Create or update a resource by PUTting the given object to the URI.
* <p>
* URI Template variables are expanded using the given URI variables, if any.
* <p>
* The {@code request} parameter can be a {@link HttpEntity} in order to add
* additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be PUT, may be {@code null}
* @param urlVariables the variables to expand the template
* @throws RestClientException on client-side HTTP error
* @see HttpEntity
* @see RestTemplate#put(java.lang.String, java.lang.Object, java.lang.Object[])
*/
public void put(String url, Object request, Object... urlVariables)
throws RestClientException {
this.restTemplate.put(url, request, urlVariables);
}
/**
* Creates a new resource by PUTting the given object to URI template.
* <p>
* URI Template variables are expanded using the given map.
* <p>
* The {@code request} parameter can be a {@link HttpEntity} in order to add
* additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be PUT, may be {@code null}
* @param urlVariables the variables to expand the template
* @throws RestClientException on client-side HTTP error
* @see HttpEntity
* @see RestTemplate#put(java.lang.String, java.lang.Object, java.util.Map)
*/
public void put(String url, Object request, Map<String, ?> urlVariables)
throws RestClientException {
this.restTemplate.put(url, request, urlVariables);
}
/**
* Creates a new resource by PUTting the given object to URL.
* <p>
* The {@code request} parameter can be a {@link HttpEntity} in order to add
* additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be PUT, may be {@code null}
* @throws RestClientException on client-side HTTP error
* @see HttpEntity
* @see RestTemplate#put(java.net.URI, java.lang.Object)
*/
public void put(URI url, Object request) throws RestClientException {
this.restTemplate.put(applyRootUriIfNecessary(url), request);
}
/**
* Update a resource by PATCHing the given object to the URI template, and returns the
* representation found in the response.
* <p>
* URI Template variables are expanded using the given URI variables, if any.
* <p>
* The {@code request} parameter can be a {@link HttpEntity} in order to add
* additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be PATCHed, may be {@code null}
* @param responseType the type of the return value
* @param uriVariables the variables to expand the template
* @param <T> the type of the return value
* @return the converted object
* @throws RestClientException on client-side HTTP error
* @since 1.4.4
* @see HttpEntity
*/
public <T> T patchForObject(String url, Object request, Class<T> responseType,
Object... uriVariables) throws RestClientException {
return this.restTemplate.patchForObject(url, request, responseType, uriVariables);
}
/**
* Update a resource by PATCHing the given object to the URI template, and returns the
* representation found in the response.
* <p>
* URI Template variables are expanded using the given map.
* <p>
* The {@code request} parameter can be a {@link HttpEntity} in order to add
* additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be PATCHed, may be {@code null}
* @param responseType the type of the return value
* @param uriVariables the variables to expand the template
* @param <T> the type of the return value
* @return the converted object
* @throws RestClientException on client-side HTTP error
* @since 1.4.4
* @see HttpEntity
*/
public <T> T patchForObject(String url, Object request, Class<T> responseType,
Map<String, ?> uriVariables) throws RestClientException {
return this.restTemplate.patchForObject(url, request, responseType, uriVariables);
}
/**
* Update a resource by PATCHing the given object to the URL, and returns the
* representation found in the response.
* <p>
* The {@code request} parameter can be a {@link HttpEntity} in order to add
* additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be POSTed, may be {@code null}
* @param responseType the type of the return value
* @param <T> the type of the return value
* @return the converted object
* @throws RestClientException on client-side HTTP error
* @since 1.4.4
* @see HttpEntity
*/
public <T> T patchForObject(URI url, Object request, Class<T> responseType)
throws RestClientException {
return this.restTemplate.patchForObject(applyRootUriIfNecessary(url), request,
responseType);
}
/**
* Delete the resources at the specified URI.
* <p>
* URI Template variables are expanded using the given URI variables, if any.
* @param url the URL
* @param urlVariables the variables to expand in the template
* @throws RestClientException on client-side HTTP error
* @see RestTemplate#delete(java.lang.String, java.lang.Object[])
*/
public void delete(String url, Object... urlVariables) throws RestClientException {
this.restTemplate.delete(url, urlVariables);
}
/**
* Delete the resources at the specified URI.
* <p>
* URI Template variables are expanded using the given map.
* @param url the URL
* @param urlVariables the variables to expand the template
* @throws RestClientException on client-side HTTP error
* @see RestTemplate#delete(java.lang.String, java.util.Map)
*/
public void delete(String url, Map<String, ?> urlVariables)
throws RestClientException {
this.restTemplate.delete(url, urlVariables);
}
/**
* Delete the resources at the specified URL.
* @param url the URL
* @throws RestClientException on client-side HTTP error
* @see RestTemplate#delete(java.net.URI)
*/
public void delete(URI url) throws RestClientException {
this.restTemplate.delete(applyRootUriIfNecessary(url));
}
/**
* Return the value of the Allow header for the given URI.
* <p>
* URI Template variables are expanded using the given URI variables, if any.
* @param url the URL
* @param urlVariables the variables to expand in the template
* @return the value of the allow header
* @throws RestClientException on client-side HTTP error
* @see RestTemplate#optionsForAllow(java.lang.String, java.lang.Object[])
*/
public Set<HttpMethod> optionsForAllow(String url, Object... urlVariables)
throws RestClientException {
return this.restTemplate.optionsForAllow(url, urlVariables);
}
/**
* Return the value of the Allow header for the given URI.
* <p>
* URI Template variables are expanded using the given map.
* @param url the URL
* @param urlVariables the variables to expand in the template
* @return the value of the allow header
* @throws RestClientException on client-side HTTP error
* @see RestTemplate#optionsForAllow(java.lang.String, java.util.Map)
*/
public Set<HttpMethod> optionsForAllow(String url, Map<String, ?> urlVariables)
throws RestClientException {
return this.restTemplate.optionsForAllow(url, urlVariables);
}
/**
* Return the value of the Allow header for the given URL.
* @param url the URL
* @return the value of the allow header
* @throws RestClientException on client-side HTTP error
* @see RestTemplate#optionsForAllow(java.net.URI)
*/
public Set<HttpMethod> optionsForAllow(URI url) throws RestClientException {
return this.restTemplate.optionsForAllow(applyRootUriIfNecessary(url));
}
/**
* Execute the HTTP method to the given URI template, writing the given request entity
* to the request, and returns the response as {@link ResponseEntity}.
* <p>
* URI Template variables are expanded using the given URI variables, if any.
* @param url the URL
* @param method the HTTP method (GET, POST, etc)
* @param requestEntity the entity (headers and/or body) to write to the request, may
* be {@code null}
* @param responseType the type of the return value
* @param urlVariables the variables to expand in the template
* @param <T> the type of the return value
* @return the response as entity
* @throws RestClientException on client-side HTTP error
* @see RestTemplate#exchange(java.lang.String, org.springframework.http.HttpMethod,
* org.springframework.http.HttpEntity, java.lang.Class, java.lang.Object[])
*/
public <T> ResponseEntity<T> exchange(String url, HttpMethod method,
HttpEntity<?> requestEntity, Class<T> responseType, Object... urlVariables)
throws RestClientException {
return this.restTemplate.exchange(url, method, requestEntity, responseType,
urlVariables);
}
/**
* Execute the HTTP method to the given URI template, writing the given request entity
* to the request, and returns the response as {@link ResponseEntity}.
* <p>
* URI Template variables are expanded using the given URI variables, if any.
* @param url the URL
* @param method the HTTP method (GET, POST, etc)
* @param requestEntity the entity (headers and/or body) to write to the request, may
* be {@code null}
* @param responseType the type of the return value
* @param urlVariables the variables to expand in the template
* @param <T> the type of the return value
* @return the response as entity
* @throws RestClientException on client-side HTTP error
* @see RestTemplate#exchange(java.lang.String, org.springframework.http.HttpMethod,
* org.springframework.http.HttpEntity, java.lang.Class, java.util.Map)
*/
public <T> ResponseEntity<T> exchange(String url, HttpMethod method,
HttpEntity<?> requestEntity, Class<T> responseType,
Map<String, ?> urlVariables) throws RestClientException {
return this.restTemplate.exchange(url, method, requestEntity, responseType,
urlVariables);
}
/**
* Execute the HTTP method to the given URI template, writing the given request entity
* to the request, and returns the response as {@link ResponseEntity}.
* @param url the URL
* @param method the HTTP method (GET, POST, etc)
* @param requestEntity the entity (headers and/or body) to write to the request, may
* be {@code null}
* @param responseType the type of the return value
* @param <T> the type of the return value
* @return the response as entity
* @throws RestClientException on client-side HTTP error
* @see RestTemplate#exchange(java.net.URI, org.springframework.http.HttpMethod,
* org.springframework.http.HttpEntity, java.lang.Class)
*/
public <T> ResponseEntity<T> exchange(URI url, HttpMethod method,
HttpEntity<?> requestEntity, Class<T> responseType)
throws RestClientException {
return this.restTemplate.exchange(applyRootUriIfNecessary(url), method,
requestEntity, responseType);
}
/**
* Execute the HTTP method to the given URI template, writing the given request entity
* to the request, and returns the response as {@link ResponseEntity}. The given
* {@link ParameterizedTypeReference} is used to pass generic type information:
* <pre class="code">
* ParameterizedTypeReference<List<MyBean>> myBean = new ParameterizedTypeReference<List<MyBean>>() {};
* ResponseEntity<List<MyBean>> response = template.exchange("http://example.com",HttpMethod.GET, null, myBean);
* </pre>
* @param url the URL
* @param method the HTTP method (GET, POST, etc)
* @param requestEntity the entity (headers and/or body) to write to the request, may
* be {@code null}
* @param responseType the type of the return value
* @param urlVariables the variables to expand in the template
* @param <T> the type of the return value
* @return the response as entity
* @throws RestClientException on client-side HTTP error
* @see RestTemplate#exchange(java.lang.String, org.springframework.http.HttpMethod,
* org.springframework.http.HttpEntity,
* org.springframework.core.ParameterizedTypeReference, java.lang.Object[])
*/
public <T> ResponseEntity<T> exchange(String url, HttpMethod method,
HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType,
Object... urlVariables) throws RestClientException {
return this.restTemplate.exchange(url, method, requestEntity, responseType,
urlVariables);
}
/**
* Execute the HTTP method to the given URI template, writing the given request entity
* to the request, and returns the response as {@link ResponseEntity}. The given
* {@link ParameterizedTypeReference} is used to pass generic type information:
* <pre class="code">
* ParameterizedTypeReference<List<MyBean>> myBean = new ParameterizedTypeReference<List<MyBean>>() {};
* ResponseEntity<List<MyBean>> response = template.exchange("http://example.com",HttpMethod.GET, null, myBean);
* </pre>
* @param url the URL
* @param method the HTTP method (GET, POST, etc)
* @param requestEntity the entity (headers and/or body) to write to the request, may
* be {@code null}
* @param responseType the type of the return value
* @param urlVariables the variables to expand in the template
* @param <T> the type of the return value
* @return the response as entity
* @throws RestClientException on client-side HTTP error
* @see RestTemplate#exchange(java.lang.String, org.springframework.http.HttpMethod,
* org.springframework.http.HttpEntity,
* org.springframework.core.ParameterizedTypeReference, java.util.Map)
*/
public <T> ResponseEntity<T> exchange(String url, HttpMethod method,
HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType,
Map<String, ?> urlVariables) throws RestClientException {
return this.restTemplate.exchange(url, method, requestEntity, responseType,
urlVariables);
}
/**
* Execute the HTTP method to the given URI template, writing the given request entity
* to the request, and returns the response as {@link ResponseEntity}. The given
* {@link ParameterizedTypeReference} is used to pass generic type information:
* <pre class="code">
* ParameterizedTypeReference<List<MyBean>> myBean = new ParameterizedTypeReference<List<MyBean>>() {};
* ResponseEntity<List<MyBean>> response = template.exchange("http://example.com",HttpMethod.GET, null, myBean);
* </pre>
* @param url the URL
* @param method the HTTP method (GET, POST, etc)
* @param requestEntity the entity (headers and/or body) to write to the request, may
* be {@code null}
* @param responseType the type of the return value
* @param <T> the type of the return value
* @return the response as entity
* @throws RestClientException on client-side HTTP error
* @see RestTemplate#exchange(java.net.URI, org.springframework.http.HttpMethod,
* org.springframework.http.HttpEntity,
* org.springframework.core.ParameterizedTypeReference)
*/
public <T> ResponseEntity<T> exchange(URI url, HttpMethod method,
HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType)
throws RestClientException {
return this.restTemplate.exchange(applyRootUriIfNecessary(url), method,
requestEntity, responseType);
}
/**
* Execute the request specified in the given {@link RequestEntity} and return the
* response as {@link ResponseEntity}. Typically used in combination with the static
* builder methods on {@code RequestEntity}, for instance: <pre class="code">
* MyRequest body = ...
* RequestEntity request = RequestEntity.post(new URI("http://example.com/foo")).accept(MediaType.APPLICATION_JSON).body(body);
* ResponseEntity<MyResponse> response = template.exchange(request, MyResponse.class);
* </pre>
* @param requestEntity the entity to write to the request
* @param responseType the type of the return value
* @param <T> the type of the return value
* @return the response as entity
* @throws RestClientException on client-side HTTP error
* @see RestTemplate#exchange(org.springframework.http.RequestEntity, java.lang.Class)
*/
public <T> ResponseEntity<T> exchange(RequestEntity<?> requestEntity,
Class<T> responseType) throws RestClientException {
return this.restTemplate.exchange(
createRequestEntityWithRootAppliedUri(requestEntity), responseType);
}
/**
* Execute the request specified in the given {@link RequestEntity} and return the
* response as {@link ResponseEntity}. The given {@link ParameterizedTypeReference} is
* used to pass generic type information: <pre class="code">
* MyRequest body = ...
* RequestEntity request = RequestEntity.post(new URI("http://example.com/foo")).accept(MediaType.APPLICATION_JSON).body(body);
* ParameterizedTypeReference<List<MyResponse>> myBean = new ParameterizedTypeReference<List<MyResponse>>() {};
* ResponseEntity<List<MyResponse>> response = template.exchange(request, myBean);
* </pre>
* @param requestEntity the entity to write to the request
* @param responseType the type of the return value
* @param <T> the type of the return value
* @return the response as entity
* @throws RestClientException on client-side HTTP error
* @see RestTemplate#exchange(org.springframework.http.RequestEntity,
* org.springframework.core.ParameterizedTypeReference)
*/
public <T> ResponseEntity<T> exchange(RequestEntity<?> requestEntity,
ParameterizedTypeReference<T> responseType) throws RestClientException {
return this.restTemplate.exchange(
createRequestEntityWithRootAppliedUri(requestEntity), responseType);
}
/**
* Execute the HTTP method to the given URI template, preparing the request with the
* {@link RequestCallback}, and reading the response with a {@link ResponseExtractor}.
* <p>
* URI Template variables are expanded using the given URI variables, if any.
* @param url the URL
* @param method the HTTP method (GET, POST, etc)
* @param requestCallback object that prepares the request
* @param responseExtractor object that extracts the return value from the response
* @param urlVariables the variables to expand in the template
* @param <T> the type of the return value
* @return an arbitrary object, as returned by the {@link ResponseExtractor}
* @throws RestClientException on client-side HTTP error
* @see RestTemplate#execute(java.lang.String, org.springframework.http.HttpMethod,
* org.springframework.web.client.RequestCallback,
* org.springframework.web.client.ResponseExtractor, java.lang.Object[])
*/
public <T> T execute(String url, HttpMethod method, RequestCallback requestCallback,
ResponseExtractor<T> responseExtractor, Object... urlVariables)
throws RestClientException {
return this.restTemplate.execute(url, method, requestCallback, responseExtractor,
urlVariables);
}
/**
* Execute the HTTP method to the given URI template, preparing the request with the
* {@link RequestCallback}, and reading the response with a {@link ResponseExtractor}.
* <p>
* URI Template variables are expanded using the given URI variables map.
* @param url the URL
* @param method the HTTP method (GET, POST, etc)
* @param requestCallback object that prepares the request
* @param responseExtractor object that extracts the return value from the response
* @param urlVariables the variables to expand in the template
* @param <T> the type of the return value
* @return an arbitrary object, as returned by the {@link ResponseExtractor}
* @throws RestClientException on client-side HTTP error
* @see RestTemplate#execute(java.lang.String, org.springframework.http.HttpMethod,
* org.springframework.web.client.RequestCallback,
* org.springframework.web.client.ResponseExtractor, java.util.Map)
*/
public <T> T execute(String url, HttpMethod method, RequestCallback requestCallback,
ResponseExtractor<T> responseExtractor, Map<String, ?> urlVariables)
throws RestClientException {
return this.restTemplate.execute(url, method, requestCallback, responseExtractor,
urlVariables);
}
/**
* Execute the HTTP method to the given URL, preparing the request with the
* {@link RequestCallback}, and reading the response with a {@link ResponseExtractor}.
* @param url the URL
* @param method the HTTP method (GET, POST, etc)
* @param requestCallback object that prepares the request
* @param responseExtractor object that extracts the return value from the response
* @param <T> the type of the return value
* @return an arbitrary object, as returned by the {@link ResponseExtractor}
* @throws RestClientException on client-side HTTP error
* @see RestTemplate#execute(java.net.URI, org.springframework.http.HttpMethod,
* org.springframework.web.client.RequestCallback,
* org.springframework.web.client.ResponseExtractor)
*/
public <T> T execute(URI url, HttpMethod method, RequestCallback requestCallback,
ResponseExtractor<T> responseExtractor) throws RestClientException {
return this.restTemplate.execute(applyRootUriIfNecessary(url), method,
requestCallback, responseExtractor);
}
/**
* Returns the underlying {@link RestTemplate} that is actually used to perform the
* REST operations.
* @return the restTemplate
*/
public RestTemplate getRestTemplate() {
return this.restTemplate;
}
/**
* Creates a new {@code TestRestTemplate} with the same configuration as this one,
* except that it will send basic authorization headers using the given
* {@code username} and {@code password}.
* @param username the username
* @param password the password
* @return the new template
* @since 1.4.1
*/
public TestRestTemplate withBasicAuth(String username, String password) {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setMessageConverters(getRestTemplate().getMessageConverters());
restTemplate.setInterceptors(getRestTemplate().getInterceptors());
restTemplate.setRequestFactory(getRestTemplate().getRequestFactory());
restTemplate.setUriTemplateHandler(getRestTemplate().getUriTemplateHandler());
TestRestTemplate testRestTemplate = new TestRestTemplate(restTemplate, username,
password, this.httpClientOptions);
testRestTemplate.getRestTemplate()
.setErrorHandler(getRestTemplate().getErrorHandler());
return testRestTemplate;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private RequestEntity<?> createRequestEntityWithRootAppliedUri(
RequestEntity<?> requestEntity) {
return new RequestEntity(requestEntity.getBody(), requestEntity.getHeaders(),
requestEntity.getMethod(),
applyRootUriIfNecessary(requestEntity.getUrl()), requestEntity.getType());
}
private URI applyRootUriIfNecessary(URI uri) {
UriTemplateHandler uriTemplateHandler = this.restTemplate.getUriTemplateHandler();
if ((uriTemplateHandler instanceof RootUriTemplateHandler)
&& uri.toString().startsWith("/")) {
return URI.create(((RootUriTemplateHandler) uriTemplateHandler).getRootUri()
+ uri.toString());
}
return uri;
}
/**
* Options used to customize the Apache Http Client if it is used.
*/
public enum HttpClientOption {
/**
* Enable cookies.
*/
ENABLE_COOKIES,
/**
* Enable redirects.
*/
ENABLE_REDIRECTS,
/**
* Use a {@link SSLConnectionSocketFactory} with {@link TrustSelfSignedStrategy}.
*/
SSL
}
/**
* {@link HttpComponentsClientHttpRequestFactory} to apply customizations.
*/
protected static class CustomHttpComponentsClientHttpRequestFactory
extends HttpComponentsClientHttpRequestFactory {
private final String cookieSpec;
private final boolean enableRedirects;
public CustomHttpComponentsClientHttpRequestFactory(
HttpClientOption[] httpClientOptions) {
Set<HttpClientOption> options = new HashSet<>(
Arrays.asList(httpClientOptions));
this.cookieSpec = (options.contains(HttpClientOption.ENABLE_COOKIES)
? CookieSpecs.STANDARD : CookieSpecs.IGNORE_COOKIES);
this.enableRedirects = options.contains(HttpClientOption.ENABLE_REDIRECTS);
if (options.contains(HttpClientOption.SSL)) {
setHttpClient(createSslHttpClient());
}
}
private HttpClient createSslHttpClient() {
try {
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
new SSLContextBuilder()
.loadTrustMaterial(null, new TrustSelfSignedStrategy())
.build());
return HttpClients.custom().setSSLSocketFactory(socketFactory).build();
}
catch (Exception ex) {
throw new IllegalStateException("Unable to create SSL HttpClient", ex);
}
}
@Override
protected HttpContext createHttpContext(HttpMethod httpMethod, URI uri) {
HttpClientContext context = HttpClientContext.create();
context.setRequestConfig(getRequestConfig());
return context;
}
protected RequestConfig getRequestConfig() {
Builder builder = RequestConfig.custom().setCookieSpec(this.cookieSpec)
.setAuthenticationEnabled(false)
.setRedirectsEnabled(this.enableRedirects);
return builder.build();
}
}
private static class NoOpResponseErrorHandler extends DefaultResponseErrorHandler {
@Override
public void handleError(ClientHttpResponse response) throws IOException {
}
}
}