diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/CheckLinks.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/CheckLinks.java index e7dbcf50566..085186845a8 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/bom/CheckLinks.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/CheckLinks.java @@ -28,11 +28,10 @@ import org.gradle.api.DefaultTask; import org.gradle.api.tasks.TaskAction; import org.gradle.internal.impldep.org.apache.http.client.config.CookieSpecs; -import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; -import org.springframework.web.client.NoOpResponseErrorHandler; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestClient; +import org.springframework.web.client.RestClient.ResponseSpec.ErrorHandler; /** * Task to check that links are working. @@ -42,6 +41,9 @@ import org.springframework.web.client.RestTemplate; */ public abstract class CheckLinks extends DefaultTask { + private static final ErrorHandler NOOP_ERROR_HANDLER = (request, response) -> { + }; + private final BomExtension bom; @Inject @@ -54,14 +56,16 @@ public abstract class CheckLinks extends DefaultTask { RequestConfig config = RequestConfig.custom().setCookieSpec(CookieSpecs.IGNORE_COOKIES).build(); CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(config).build(); HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient); - RestTemplate restTemplate = new RestTemplate(requestFactory); - restTemplate.setErrorHandler(new NoOpResponseErrorHandler()); + RestClient restClient = RestClient.builder() + .requestFactory(requestFactory) + .defaultStatusHandler((status) -> true, NOOP_ERROR_HANDLER) + .build(); for (Library library : this.bom.getLibraries()) { library.getLinks().forEach((name, links) -> links.forEach((link) -> { URI uri; try { uri = new URI(link.url(library)); - ResponseEntity response = restTemplate.exchange(uri, HttpMethod.HEAD, null, String.class); + ResponseEntity response = restClient.head().uri(uri).retrieve().toEntity(String.class); System.out.printf("[%3d] %s - %s (%s)%n", response.getStatusCode().value(), library.getName(), name, uri); } diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/MavenMetadataVersionResolver.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/MavenMetadataVersionResolver.java index ecc70b4314e..c9030510e76 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/MavenMetadataVersionResolver.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/MavenMetadataVersionResolver.java @@ -18,7 +18,6 @@ package org.springframework.boot.build.bom.bomr; import java.net.URI; import java.util.Collection; -import java.util.Collections; import java.util.HashSet; import java.util.Set; import java.util.SortedSet; @@ -37,13 +36,10 @@ import org.w3c.dom.NodeList; import org.springframework.boot.build.bom.bomr.version.DependencyVersion; import org.springframework.boot.build.xml.XmlDocument; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.web.client.HttpClientErrorException; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestClient; import org.springframework.web.util.UriComponentsBuilder; /** @@ -54,16 +50,19 @@ import org.springframework.web.util.UriComponentsBuilder; */ final class MavenMetadataVersionResolver implements VersionResolver { - private final RestTemplate rest; + private final RestClient rest; private final Collection repositories; MavenMetadataVersionResolver(Collection repositories) { - this(new RestTemplate(Collections.singletonList(new StringHttpMessageConverter())), repositories); + this(RestClient.builder() + .configureMessageConverters( + (converters) -> converters.disableDefaults().withStringConverter(new StringHttpMessageConverter())) + .build(), repositories); } - MavenMetadataVersionResolver(RestTemplate restTemplate, Collection repositories) { - this.rest = restTemplate; + MavenMetadataVersionResolver(RestClient restClient, Collection repositories) { + this.rest = restClient; this.repositories = repositories; } @@ -83,14 +82,13 @@ final class MavenMetadataVersionResolver implements VersionResolver { .build() .toUri(); try { - HttpHeaders headers = new HttpHeaders(); - PasswordCredentials credentials = credentialsOf(repository); - String username = (credentials != null) ? credentials.getUsername() : null; - if (username != null) { - headers.setBasicAuth(username, credentials.getPassword()); - } - HttpEntity request = new HttpEntity<>(headers); - String metadata = this.rest.exchange(url, HttpMethod.GET, request, String.class).getBody(); + String metadata = this.rest.get().uri(url).headers((headers) -> { + PasswordCredentials credentials = credentialsOf(repository); + String username = (credentials != null) ? credentials.getUsername() : null; + if (username != null) { + headers.setBasicAuth(username, credentials.getPassword()); + } + }).retrieve().body(String.class); Document metadataDocument = XmlDocument.parseContent(metadata); NodeList versionNodes = (NodeList) XPathFactory.newInstance() .newXPath() diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/ReleaseSchedule.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/ReleaseSchedule.java index 68414bdd35f..2684e4057d0 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/ReleaseSchedule.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/ReleaseSchedule.java @@ -28,8 +28,7 @@ import java.util.regex.Pattern; import org.springframework.boot.build.bom.bomr.version.DependencyVersion; import org.springframework.http.ResponseEntity; import org.springframework.util.LinkedCaseInsensitiveMap; -import org.springframework.web.client.RestOperations; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestClient; /** * Release schedule for Spring projects, retrieved from @@ -41,20 +40,22 @@ class ReleaseSchedule { private static final Pattern LIBRARY_AND_VERSION = Pattern.compile("([A-Za-z0-9 ]+) ([0-9A-Za-z.-]+)"); - private final RestOperations rest; + private final RestClient rest; ReleaseSchedule() { - this(new RestTemplate()); + this(RestClient.create()); } - ReleaseSchedule(RestOperations rest) { + ReleaseSchedule(RestClient rest) { this.rest = rest; } @SuppressWarnings({ "unchecked", "rawtypes" }) Map> releasesBetween(OffsetDateTime start, OffsetDateTime end) { - ResponseEntity response = this.rest - .getForEntity("https://calendar.spring.io/releases?start=" + start + "&end=" + end, List.class); + ResponseEntity response = this.rest.get() + .uri("https://calendar.spring.io/releases?start=" + start + "&end=" + end) + .retrieve() + .toEntity(List.class); List> body = response.getBody(); Map> releasesByLibrary = new LinkedCaseInsensitiveMap<>(); body.stream() diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/Issue.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/Issue.java index 31d2dfea79f..1372d4e73d0 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/Issue.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/Issue.java @@ -20,7 +20,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestClient; /** * Minimal representation of a GitHub issue. @@ -29,7 +29,7 @@ import org.springframework.web.client.RestTemplate; */ public class Issue { - private final RestTemplate rest; + private final RestClient rest; private final int number; @@ -37,7 +37,7 @@ public class Issue { private final State state; - Issue(RestTemplate rest, int number, String title, State state) { + Issue(RestClient rest, int number, String title, State state) { this.rest = rest; this.number = number; this.title = title; @@ -62,7 +62,7 @@ public class Issue { */ public void label(List labels) { Map> body = Collections.singletonMap("labels", labels); - this.rest.put("issues/" + this.number + "/labels", body); + this.rest.put().uri("issues/" + this.number + "/labels").body(body).retrieve().toBodilessEntity(); } public enum State { diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/StandardGitHub.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/StandardGitHub.java index 331cce148bf..dcd9ae5aada 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/StandardGitHub.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/StandardGitHub.java @@ -17,13 +17,10 @@ package org.springframework.boot.build.bom.bomr.github; import java.util.Base64; -import java.util.Collections; import org.springframework.http.MediaType; import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.util.DefaultUriBuilderFactory; -import org.springframework.web.util.UriTemplateHandler; +import org.springframework.web.client.RestClient; /** * Standard implementation of {@link GitHub}. @@ -43,24 +40,24 @@ final class StandardGitHub implements GitHub { @Override public GitHubRepository getRepository(String organization, String name) { - RestTemplate restTemplate = createRestTemplate(); - restTemplate.getInterceptors().add((request, body, execution) -> { - request.getHeaders().add("User-Agent", StandardGitHub.this.username); - request.getHeaders() - .add("Authorization", "Basic " + Base64.getEncoder() - .encodeToString((StandardGitHub.this.username + ":" + StandardGitHub.this.password).getBytes())); - request.getHeaders().add("Accept", MediaType.APPLICATION_JSON_VALUE); - return execution.execute(request, body); - }); - UriTemplateHandler uriTemplateHandler = new DefaultUriBuilderFactory( - "https://api.github.com/repos/" + organization + "/" + name + "/"); - restTemplate.setUriTemplateHandler(uriTemplateHandler); - return new StandardGitHubRepository(restTemplate); + return new StandardGitHubRepository(createRestClient(organization, name)); } - @SuppressWarnings({ "deprecation", "removal" }) - private RestTemplate createRestTemplate() { - return new RestTemplate(Collections.singletonList(new JacksonJsonHttpMessageConverter())); + private RestClient createRestClient(String organization, String name) { + return RestClient.builder() + .baseUrl("https://api.github.com/repos/" + organization + "/" + name + "/") + .configureMessageConverters((converters) -> converters.disableDefaults() + .withJsonConverter(new JacksonJsonHttpMessageConverter())) + .requestInterceptor((request, body, execution) -> { + request.getHeaders().add("User-Agent", StandardGitHub.this.username); + request.getHeaders() + .add("Authorization", "Basic " + Base64.getEncoder() + .encodeToString( + (StandardGitHub.this.username + ":" + StandardGitHub.this.password).getBytes())); + request.getHeaders().add("Accept", MediaType.APPLICATION_JSON_VALUE); + return execution.execute(request, body); + }) + .build(); } } diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/StandardGitHubRepository.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/StandardGitHubRepository.java index 60fc43aded6..c766f38ec1b 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/StandardGitHubRepository.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/StandardGitHubRepository.java @@ -27,8 +27,8 @@ import java.util.function.Function; import org.springframework.http.ResponseEntity; import org.springframework.web.client.HttpClientErrorException.Forbidden; +import org.springframework.web.client.RestClient; import org.springframework.web.client.RestClientException; -import org.springframework.web.client.RestTemplate; /** * Standard implementation of {@link GitHubRepository}. @@ -37,10 +37,10 @@ import org.springframework.web.client.RestTemplate; */ final class StandardGitHubRepository implements GitHubRepository { - private final RestTemplate rest; + private final RestClient rest; - StandardGitHubRepository(RestTemplate restTemplate) { - this.rest = restTemplate; + StandardGitHubRepository(RestClient restClient) { + this.rest = restClient; } @Override @@ -56,7 +56,11 @@ final class StandardGitHubRepository implements GitHubRepository { } requestBody.put("body", body); try { - ResponseEntity response = this.rest.postForEntity("issues", requestBody, Map.class); + ResponseEntity response = this.rest.post() + .uri("issues") + .body(requestBody) + .retrieve() + .toEntity(Map.class); // See gh-30304 sleep(Duration.ofSeconds(3)); return (Integer) response.getBody().get("number"); @@ -92,7 +96,7 @@ final class StandardGitHubRepository implements GitHubRepository { @SuppressWarnings({ "rawtypes", "unchecked" }) private List get(String name, Function, T> mapper) { - ResponseEntity response = this.rest.getForEntity(name, List.class); + ResponseEntity response = this.rest.get().uri(name).retrieve().toEntity(List.class); return ((List>) response.getBody()).stream().map(mapper).toList(); } diff --git a/buildSrc/src/test/java/org/springframework/boot/build/bom/bomr/ReleaseScheduleTests.java b/buildSrc/src/test/java/org/springframework/boot/build/bom/bomr/ReleaseScheduleTests.java index a74c3d30e17..40abf922f2b 100644 --- a/buildSrc/src/test/java/org/springframework/boot/build/bom/bomr/ReleaseScheduleTests.java +++ b/buildSrc/src/test/java/org/springframework/boot/build/bom/bomr/ReleaseScheduleTests.java @@ -26,7 +26,7 @@ import org.springframework.boot.build.bom.bomr.ReleaseSchedule.Release; import org.springframework.core.io.ClassPathResource; import org.springframework.http.MediaType; import org.springframework.test.web.client.MockRestServiceServer; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestClient; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; @@ -39,11 +39,11 @@ import static org.springframework.test.web.client.response.MockRestResponseCreat */ class ReleaseScheduleTests { - private final RestTemplate rest = new RestTemplate(); + private final RestClient.Builder restBuilder = RestClient.builder(); - private final ReleaseSchedule releaseSchedule = new ReleaseSchedule(this.rest); + private final MockRestServiceServer server = MockRestServiceServer.bindTo(this.restBuilder).build(); - private final MockRestServiceServer server = MockRestServiceServer.bindTo(this.rest).build(); + private final ReleaseSchedule releaseSchedule = new ReleaseSchedule(this.restBuilder.build()); @Test void releasesBetween() { diff --git a/integration-test/spring-boot-integration-tests/src/test/java/org/springframework/boot/web/servlet/support/ErrorPageFilterIntegrationTests.java b/integration-test/spring-boot-integration-tests/src/test/java/org/springframework/boot/web/servlet/support/ErrorPageFilterIntegrationTests.java index 35b803d4e4a..7b84c9beb5b 100644 --- a/integration-test/spring-boot-integration-tests/src/test/java/org/springframework/boot/web/servlet/support/ErrorPageFilterIntegrationTests.java +++ b/integration-test/spring-boot-integration-tests/src/test/java/org/springframework/boot/web/servlet/support/ErrorPageFilterIntegrationTests.java @@ -45,7 +45,7 @@ import org.springframework.test.context.support.AbstractContextLoader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestClient; import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; @@ -93,9 +93,11 @@ class ErrorPageFilterIntegrationTests { private void doTest(AnnotationConfigServletWebServerApplicationContext context, String resourcePath, HttpStatus status) throws Exception { int port = context.getWebServer().getPort(); - RestTemplate template = new RestTemplate(); - ResponseEntity entity = template.getForEntity(new URI("http://localhost:" + port + resourcePath), - String.class); + RestClient restClient = RestClient.create(); + ResponseEntity entity = restClient.get() + .uri(new URI("http://localhost:" + port + resourcePath)) + .retrieve() + .toEntity(String.class); assertThat(entity.getBody()).isEqualTo("Hello World"); assertThat(entity.getStatusCode()).isEqualTo(status); } diff --git a/integration-test/spring-boot-server-integration-tests/src/intTest/java/org/springframework/boot/context/embedded/EmbeddedServerContainerInvocationContextProvider.java b/integration-test/spring-boot-server-integration-tests/src/intTest/java/org/springframework/boot/context/embedded/EmbeddedServerContainerInvocationContextProvider.java index b5120389081..b77c3d1d825 100644 --- a/integration-test/spring-boot-server-integration-tests/src/intTest/java/org/springframework/boot/context/embedded/EmbeddedServerContainerInvocationContextProvider.java +++ b/integration-test/spring-boot-server-integration-tests/src/intTest/java/org/springframework/boot/context/embedded/EmbeddedServerContainerInvocationContextProvider.java @@ -19,7 +19,6 @@ package org.springframework.boot.context.embedded; import java.io.File; import java.io.IOException; import java.lang.reflect.Constructor; -import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; @@ -49,9 +48,10 @@ import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.util.FileSystemUtils; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; -import org.springframework.web.client.NoOpResponseErrorHandler; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.util.UriTemplateHandler; +import org.springframework.web.client.RestClient; +import org.springframework.web.client.RestClient.ResponseSpec.ErrorHandler; +import org.springframework.web.util.DefaultUriBuilderFactory; +import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode; /** * {@link TestTemplateInvocationContextProvider} for templated @@ -152,7 +152,7 @@ class EmbeddedServerContainerInvocationContextProvider @Override public List getAdditionalExtensions() { - return Arrays.asList(this.launcher, new RestTemplateParameterResolver(this.launcher)); + return Arrays.asList(this.launcher, new RestClientParameterResolver(this.launcher)); } @Override @@ -165,7 +165,7 @@ class EmbeddedServerContainerInvocationContextProvider if (parameterContext.getParameter().getType().equals(AbstractApplicationLauncher.class)) { return true; } - return parameterContext.getParameter().getType().equals(RestTemplate.class); + return parameterContext.getParameter().getType().equals(RestClient.class); } @Override @@ -178,41 +178,35 @@ class EmbeddedServerContainerInvocationContextProvider } - private static final class RestTemplateParameterResolver implements ParameterResolver { + private static final class RestClientParameterResolver implements ParameterResolver { + + private static final ErrorHandler NOOP_ERROR_HANDLER = (request, response) -> { + }; private final AbstractApplicationLauncher launcher; - private RestTemplateParameterResolver(AbstractApplicationLauncher launcher) { + private RestClientParameterResolver(AbstractApplicationLauncher launcher) { this.launcher = launcher; } @Override public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { - return parameterContext.getParameter().getType().equals(RestTemplate.class); + return parameterContext.getParameter().getType().equals(RestClient.class); } @Override public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { - RestTemplate rest = new RestTemplate(new HttpComponentsClientHttpRequestFactory(HttpClients.custom() - .setRetryStrategy(new DefaultHttpRequestRetryStrategy(10, TimeValue.of(1, TimeUnit.SECONDS))) - .build())); - rest.setErrorHandler(new NoOpResponseErrorHandler()); - rest.setUriTemplateHandler(new UriTemplateHandler() { - - @Override - public URI expand(String uriTemplate, Object... uriVariables) { - return URI.create("http://localhost:" + RestTemplateParameterResolver.this.launcher.getHttpPort() - + uriTemplate); - } - - @Override - public URI expand(String uriTemplate, Map uriVariables) { - return URI.create("http://localhost:" + RestTemplateParameterResolver.this.launcher.getHttpPort() - + uriTemplate); - } - - }); - return rest; + DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory( + "http://localhost:" + this.launcher.getHttpPort()); + // Do not double encode test paths + uriBuilderFactory.setEncodingMode(EncodingMode.NONE); + return RestClient.builder() + .uriBuilderFactory(uriBuilderFactory) + .requestFactory(new HttpComponentsClientHttpRequestFactory(HttpClients.custom() + .setRetryStrategy(new DefaultHttpRequestRetryStrategy(10, TimeValue.of(1, TimeUnit.SECONDS))) + .build())) + .defaultStatusHandler((status) -> true, NOOP_ERROR_HANDLER) + .build(); } } diff --git a/integration-test/spring-boot-server-integration-tests/src/intTest/java/org/springframework/boot/context/embedded/EmbeddedServletContainerJarDevelopmentIntegrationTests.java b/integration-test/spring-boot-server-integration-tests/src/intTest/java/org/springframework/boot/context/embedded/EmbeddedServletContainerJarDevelopmentIntegrationTests.java index 8418db46ec6..10ca7166aee 100644 --- a/integration-test/spring-boot-server-integration-tests/src/intTest/java/org/springframework/boot/context/embedded/EmbeddedServletContainerJarDevelopmentIntegrationTests.java +++ b/integration-test/spring-boot-server-integration-tests/src/intTest/java/org/springframework/boot/context/embedded/EmbeddedServletContainerJarDevelopmentIntegrationTests.java @@ -22,7 +22,7 @@ import org.junit.jupiter.api.condition.OS; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestClient; import static org.assertj.core.api.Assertions.assertThat; @@ -37,25 +37,31 @@ import static org.assertj.core.api.Assertions.assertThat; class EmbeddedServletContainerJarDevelopmentIntegrationTests { @TestTemplate - void metaInfResourceFromDependencyIsAvailableViaHttp(RestTemplate rest) { - ResponseEntity entity = rest.getForEntity("/nested-meta-inf-resource.txt", String.class); + void metaInfResourceFromDependencyIsAvailableViaHttp(RestClient rest) { + ResponseEntity entity = rest.get() + .uri("/nested-meta-inf-resource.txt") + .retrieve() + .toEntity(String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); } @TestTemplate @DisabledOnOs(OS.WINDOWS) - void metaInfResourceFromDependencyWithNameThatContainsReservedCharactersIsAvailableViaHttp(RestTemplate rest) { - ResponseEntity entity = rest.getForEntity( - "/nested-reserved-%21%23%24%25%26%28%29%2A%2B%2C%3A%3D%3F%40%5B%5D-meta-inf-resource.txt", - String.class); + void metaInfResourceFromDependencyWithNameThatContainsReservedCharactersIsAvailableViaHttp(RestClient rest) { + ResponseEntity entity = rest.get() + .uri("/nested-reserved-%21%23%24%25%26%28%29%2A%2B%2C%3A%3D%3F%40%5B%5D-meta-inf-resource.txt") + .retrieve() + .toEntity(String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(entity.getBody()).isEqualTo("encoded-name"); } @TestTemplate - void metaInfResourceFromDependencyIsAvailableViaServletContext(RestTemplate rest) { - ResponseEntity entity = rest.getForEntity("/servletContext?/nested-meta-inf-resource.txt", - String.class); + void metaInfResourceFromDependencyIsAvailableViaServletContext(RestClient rest) { + ResponseEntity entity = rest.get() + .uri("/servletContext?/nested-meta-inf-resource.txt") + .retrieve() + .toEntity(String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); } diff --git a/integration-test/spring-boot-server-integration-tests/src/intTest/java/org/springframework/boot/context/embedded/EmbeddedServletContainerJarPackagingIntegrationTests.java b/integration-test/spring-boot-server-integration-tests/src/intTest/java/org/springframework/boot/context/embedded/EmbeddedServletContainerJarPackagingIntegrationTests.java index 3bdf39cee8b..02f1c6274d6 100644 --- a/integration-test/spring-boot-server-integration-tests/src/intTest/java/org/springframework/boot/context/embedded/EmbeddedServletContainerJarPackagingIntegrationTests.java +++ b/integration-test/spring-boot-server-integration-tests/src/intTest/java/org/springframework/boot/context/embedded/EmbeddedServletContainerJarPackagingIntegrationTests.java @@ -22,7 +22,7 @@ import org.junit.jupiter.api.condition.OS; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestClient; import static org.assertj.core.api.Assertions.assertThat; @@ -37,51 +37,64 @@ import static org.assertj.core.api.Assertions.assertThat; class EmbeddedServletContainerJarPackagingIntegrationTests { @TestTemplate - void nestedMetaInfResourceIsAvailableViaHttp(RestTemplate rest) { - ResponseEntity entity = rest.getForEntity("/nested-meta-inf-resource.txt", String.class); + void nestedMetaInfResourceIsAvailableViaHttp(RestClient rest) { + ResponseEntity entity = rest.get() + .uri("/nested-meta-inf-resource.txt") + .retrieve() + .toEntity(String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); } @TestTemplate @DisabledOnOs(OS.WINDOWS) - void nestedMetaInfResourceWithNameThatContainsReservedCharactersIsAvailableViaHttp(RestTemplate rest) { - ResponseEntity entity = rest.getForEntity( - "/nested-reserved-%21%23%24%25%26%28%29%2A%2B%2C%3A%3D%3F%40%5B%5D-meta-inf-resource.txt", - String.class); + void nestedMetaInfResourceWithNameThatContainsReservedCharactersIsAvailableViaHttp(RestClient rest) { + ResponseEntity entity = rest.get() + .uri("/nested-reserved-%21%23%24%25%26%28%29%2A%2B%2C%3A%3D%3F%40%5B%5D-meta-inf-resource.txt") + .retrieve() + .toEntity(String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(entity.getBody()).isEqualTo("encoded-name"); } @TestTemplate - void nestedMetaInfResourceIsAvailableViaServletContext(RestTemplate rest) { - ResponseEntity entity = rest.getForEntity("/servletContext?/nested-meta-inf-resource.txt", - String.class); + void nestedMetaInfResourceIsAvailableViaServletContext(RestClient rest) { + ResponseEntity entity = rest.get() + .uri("/servletContext?/nested-meta-inf-resource.txt") + .retrieve() + .toEntity(String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); } @TestTemplate - void nestedJarIsNotAvailableViaHttp(RestTemplate rest) { - ResponseEntity entity = rest.getForEntity("/BOOT-INF/lib/resources-1.0.jar", String.class); + void nestedJarIsNotAvailableViaHttp(RestClient rest) { + ResponseEntity entity = rest.get() + .uri("/BOOT-INF/lib/resources-1.0.jar") + .retrieve() + .toEntity(String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); } @TestTemplate - void applicationClassesAreNotAvailableViaHttp(RestTemplate rest) { - ResponseEntity entity = rest - .getForEntity("/BOOT-INF/classes/com/example/ResourceHandlingApplication.class", String.class); + void applicationClassesAreNotAvailableViaHttp(RestClient rest) { + ResponseEntity entity = rest.get() + .uri("/BOOT-INF/classes/com/example/ResourceHandlingApplication.class") + .retrieve() + .toEntity(String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); } @TestTemplate - void launcherIsNotAvailableViaHttp(RestTemplate rest) { - ResponseEntity entity = rest.getForEntity("/org/springframework/boot/loader/Launcher.class", - String.class); + void launcherIsNotAvailableViaHttp(RestClient rest) { + ResponseEntity entity = rest.get() + .uri("/org/springframework/boot/loader/Launcher.class") + .retrieve() + .toEntity(String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); } @TestTemplate - void conditionalOnWarDeploymentBeanIsNotAvailableForEmbeddedServer(RestTemplate rest) { - ResponseEntity entity = rest.getForEntity("/war", String.class); + void conditionalOnWarDeploymentBeanIsNotAvailableForEmbeddedServer(RestClient rest) { + ResponseEntity entity = rest.get().uri("/war").retrieve().toEntity(String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); } diff --git a/integration-test/spring-boot-server-integration-tests/src/intTest/java/org/springframework/boot/context/embedded/EmbeddedServletContainerWarDevelopmentIntegrationTests.java b/integration-test/spring-boot-server-integration-tests/src/intTest/java/org/springframework/boot/context/embedded/EmbeddedServletContainerWarDevelopmentIntegrationTests.java index 4285abaecef..1738399353d 100644 --- a/integration-test/spring-boot-server-integration-tests/src/intTest/java/org/springframework/boot/context/embedded/EmbeddedServletContainerWarDevelopmentIntegrationTests.java +++ b/integration-test/spring-boot-server-integration-tests/src/intTest/java/org/springframework/boot/context/embedded/EmbeddedServletContainerWarDevelopmentIntegrationTests.java @@ -28,7 +28,7 @@ import org.junit.jupiter.api.condition.OS; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestClient; import static org.assertj.core.api.Assertions.assertThat; @@ -43,37 +43,43 @@ import static org.assertj.core.api.Assertions.assertThat; class EmbeddedServletContainerWarDevelopmentIntegrationTests { @TestTemplate - void metaInfResourceFromDependencyIsAvailableViaHttp(RestTemplate rest) { - ResponseEntity entity = rest.getForEntity("/nested-meta-inf-resource.txt", String.class); + void metaInfResourceFromDependencyIsAvailableViaHttp(RestClient rest) { + ResponseEntity entity = rest.get() + .uri("/nested-meta-inf-resource.txt") + .retrieve() + .toEntity(String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); } @TestTemplate @DisabledOnOs(OS.WINDOWS) - void metaInfResourceFromDependencyWithNameThatContainsReservedCharactersIsAvailableViaHttp(RestTemplate rest) { - ResponseEntity entity = rest.getForEntity( - "/nested-reserved-%21%23%24%25%26%28%29%2A%2B%2C%3A%3D%3F%40%5B%5D-meta-inf-resource.txt", - String.class); + void metaInfResourceFromDependencyWithNameThatContainsReservedCharactersIsAvailableViaHttp(RestClient rest) { + ResponseEntity entity = rest.get() + .uri("/nested-reserved-%21%23%24%25%26%28%29%2A%2B%2C%3A%3D%3F%40%5B%5D-meta-inf-resource.txt") + .retrieve() + .toEntity(String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(entity.getBody()).isEqualTo("encoded-name"); } @TestTemplate - void metaInfResourceFromDependencyIsAvailableViaServletContext(RestTemplate rest) { - ResponseEntity entity = rest.getForEntity("/servletContext?/nested-meta-inf-resource.txt", - String.class); + void metaInfResourceFromDependencyIsAvailableViaServletContext(RestClient rest) { + ResponseEntity entity = rest.get() + .uri("/servletContext?/nested-meta-inf-resource.txt") + .retrieve() + .toEntity(String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); } @TestTemplate - void webappResourcesAreAvailableViaHttp(RestTemplate rest) { - ResponseEntity entity = rest.getForEntity("/webapp-resource.txt", String.class); + void webappResourcesAreAvailableViaHttp(RestClient rest) { + ResponseEntity entity = rest.get().uri("/webapp-resource.txt").retrieve().toEntity(String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); } @TestTemplate - void loaderClassesAreNotAvailableViaResourcePaths(RestTemplate rest) { - ResponseEntity entity = rest.getForEntity("/resourcePaths", String.class); + void loaderClassesAreNotAvailableViaResourcePaths(RestClient rest) { + ResponseEntity entity = rest.get().uri("/resourcePaths").retrieve().toEntity(String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(readLines(entity.getBody())) .noneMatch((resourcePath) -> resourcePath.startsWith("/org/springframework/boot/loader")); diff --git a/integration-test/spring-boot-server-integration-tests/src/intTest/java/org/springframework/boot/context/embedded/EmbeddedServletContainerWarPackagingIntegrationTests.java b/integration-test/spring-boot-server-integration-tests/src/intTest/java/org/springframework/boot/context/embedded/EmbeddedServletContainerWarPackagingIntegrationTests.java index 6a5e727034f..1e3bb237c44 100644 --- a/integration-test/spring-boot-server-integration-tests/src/intTest/java/org/springframework/boot/context/embedded/EmbeddedServletContainerWarPackagingIntegrationTests.java +++ b/integration-test/spring-boot-server-integration-tests/src/intTest/java/org/springframework/boot/context/embedded/EmbeddedServletContainerWarPackagingIntegrationTests.java @@ -28,7 +28,7 @@ import org.junit.jupiter.api.condition.OS; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestClient; import static org.assertj.core.api.Assertions.assertThat; @@ -43,68 +43,85 @@ import static org.assertj.core.api.Assertions.assertThat; class EmbeddedServletContainerWarPackagingIntegrationTests { @TestTemplate - void nestedMetaInfResourceIsAvailableViaHttp(RestTemplate rest) { - ResponseEntity entity = rest.getForEntity("/nested-meta-inf-resource.txt", String.class); + void nestedMetaInfResourceIsAvailableViaHttp(RestClient rest) { + ResponseEntity entity = rest.get() + .uri("/nested-meta-inf-resource.txt") + .retrieve() + .toEntity(String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); } @TestTemplate @DisabledOnOs(OS.WINDOWS) - void nestedMetaInfResourceWithNameThatContainsReservedCharactersIsAvailableViaHttp(RestTemplate rest) { - ResponseEntity entity = rest.getForEntity( - "/nested-reserved-%21%23%24%25%26%28%29%2A%2B%2C%3A%3D%3F%40%5B%5D-meta-inf-resource.txt", - String.class); + void nestedMetaInfResourceWithNameThatContainsReservedCharactersIsAvailableViaHttp(RestClient rest) { + ResponseEntity entity = rest.get() + .uri("/nested-reserved-%21%23%24%25%26%28%29%2A%2B%2C%3A%3D%3F%40%5B%5D-meta-inf-resource.txt") + .retrieve() + .toEntity(String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(entity.getBody()).isEqualTo("encoded-name"); } @TestTemplate - void nestedMetaInfResourceIsAvailableViaServletContext(RestTemplate rest) { - ResponseEntity entity = rest.getForEntity("/servletContext?/nested-meta-inf-resource.txt", - String.class); + void nestedMetaInfResourceIsAvailableViaServletContext(RestClient rest) { + ResponseEntity entity = rest.get() + .uri("/servletContext?/nested-meta-inf-resource.txt") + .retrieve() + .toEntity(String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); } @TestTemplate - void nestedJarIsNotAvailableViaHttp(RestTemplate rest) { - ResponseEntity entity = rest.getForEntity("/WEB-INF/lib/resources-1.0.jar", String.class); + void nestedJarIsNotAvailableViaHttp(RestClient rest) { + ResponseEntity entity = rest.get() + .uri("/WEB-INF/lib/resources-1.0.jar") + .retrieve() + .toEntity(String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); } @TestTemplate - void applicationClassesAreNotAvailableViaHttp(RestTemplate rest) { - ResponseEntity entity = rest - .getForEntity("/WEB-INF/classes/com/example/ResourceHandlingApplication.class", String.class); + void applicationClassesAreNotAvailableViaHttp(RestClient rest) { + ResponseEntity entity = rest.get() + .uri("/WEB-INF/classes/com/example/ResourceHandlingApplication.class") + .retrieve() + .toEntity(String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); } @TestTemplate - void webappResourcesAreAvailableViaHttp(RestTemplate rest) { - ResponseEntity entity = rest.getForEntity("/webapp-resource.txt", String.class); + void webappResourcesAreAvailableViaHttp(RestClient rest) { + ResponseEntity entity = rest.get().uri("/webapp-resource.txt").retrieve().toEntity(String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); } @TestTemplate - void loaderClassesAreNotAvailableViaHttp(RestTemplate rest) { - ResponseEntity entity = rest.getForEntity("/org/springframework/boot/loader/Launcher.class", - String.class); + void loaderClassesAreNotAvailableViaHttp(RestClient rest) { + ResponseEntity entity = rest.get() + .uri("/org/springframework/boot/loader/Launcher.class") + .retrieve() + .toEntity(String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); - entity = rest.getForEntity("/org/springframework/../springframework/boot/loader/Launcher.class", String.class); + entity = rest.get() + .uri("/org/springframework/../springframework/boot/loader/Launcher.class") + .retrieve() + .toEntity(String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); } @TestTemplate - void loaderClassesAreNotAvailableViaResourcePaths(RestTemplate rest) { - ResponseEntity entity = rest.getForEntity("/resourcePaths", String.class); + void loaderClassesAreNotAvailableViaResourcePaths(RestClient rest) { + ResponseEntity entity = rest.get().uri("/resourcePaths").retrieve().toEntity(String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(readLines(entity.getBody())) .noneMatch((resourcePath) -> resourcePath.startsWith("/org/springframework/boot/loader")); } @TestTemplate - void conditionalOnWarDeploymentBeanIsNotAvailableForEmbeddedServer(RestTemplate rest) { - assertThat(rest.getForEntity("/always", String.class).getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(rest.getForEntity("/conditionalOnWar", String.class).getStatusCode()) + void conditionalOnWarDeploymentBeanIsNotAvailableForEmbeddedServer(RestClient rest) { + assertThat(rest.get().uri("/always").retrieve().toEntity(String.class).getStatusCode()) + .isEqualTo(HttpStatus.OK); + assertThat(rest.get().uri("/conditionalOnWar").retrieve().toEntity(String.class).getStatusCode()) .isEqualTo(HttpStatus.NOT_FOUND); } diff --git a/integration-test/spring-boot-test-integration-tests/src/test/java/org/springframework/boot/web/server/test/AbstractSpringBootTestWebServerWebEnvironmentTests.java b/integration-test/spring-boot-test-integration-tests/src/test/java/org/springframework/boot/web/server/test/AbstractSpringBootTestWebServerWebEnvironmentTests.java index 866fdf33890..943f4e89e81 100644 --- a/integration-test/spring-boot-test-integration-tests/src/test/java/org/springframework/boot/web/server/test/AbstractSpringBootTestWebServerWebEnvironmentTests.java +++ b/integration-test/spring-boot-test-integration-tests/src/test/java/org/springframework/boot/web/server/test/AbstractSpringBootTestWebServerWebEnvironmentTests.java @@ -34,7 +34,7 @@ import org.springframework.test.web.servlet.client.RestTestClient; import org.springframework.test.web.servlet.client.RestTestClient.ResponseSpec; import org.springframework.test.web.servlet.client.assertj.RestTestClientResponse; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestClient; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; import org.springframework.web.servlet.DispatcherServlet; @@ -81,7 +81,11 @@ abstract class AbstractSpringBootTestWebServerWebEnvironmentTests { @Test void runAndTestHttpEndpoint() { assertThat(this.port).isNotEqualTo(8080).isNotZero(); - String body = new RestTemplate().getForObject("http://localhost:" + this.port + "/", String.class); + String body = RestClient.create() + .get() + .uri("http://localhost:" + this.port + "/") + .retrieve() + .body(String.class); assertThat(body).isEqualTo("Hello World"); } diff --git a/module/spring-boot-cloudfoundry/src/test/java/org/springframework/boot/cloudfoundry/autoconfigure/actuate/endpoint/servlet/SkipSslVerificationHttpRequestFactoryTests.java b/module/spring-boot-cloudfoundry/src/test/java/org/springframework/boot/cloudfoundry/autoconfigure/actuate/endpoint/servlet/SkipSslVerificationHttpRequestFactoryTests.java index bc9fb06d295..ce726b7389a 100644 --- a/module/spring-boot-cloudfoundry/src/test/java/org/springframework/boot/cloudfoundry/autoconfigure/actuate/endpoint/servlet/SkipSslVerificationHttpRequestFactoryTests.java +++ b/module/spring-boot-cloudfoundry/src/test/java/org/springframework/boot/cloudfoundry/autoconfigure/actuate/endpoint/servlet/SkipSslVerificationHttpRequestFactoryTests.java @@ -31,7 +31,7 @@ import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.client.ResourceAccessException; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestClient; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -55,12 +55,12 @@ class SkipSslVerificationHttpRequestFactoryTests { void restCallToSelfSignedServerShouldNotThrowSslException() { String httpsUrl = getHttpsUrl(); SkipSslVerificationHttpRequestFactory requestFactory = new SkipSslVerificationHttpRequestFactory(); - RestTemplate restTemplate = new RestTemplate(requestFactory); - RestTemplate otherRestTemplate = new RestTemplate(); - ResponseEntity responseEntity = restTemplate.getForEntity(httpsUrl, String.class); + RestClient restClient = RestClient.builder().requestFactory(requestFactory).build(); + RestClient otherRestClient = RestClient.create(); + ResponseEntity responseEntity = restClient.get().uri(httpsUrl).retrieve().toEntity(String.class); assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK); assertThatExceptionOfType(ResourceAccessException.class) - .isThrownBy(() -> otherRestTemplate.getForEntity(httpsUrl, String.class)) + .isThrownBy(() -> otherRestClient.get().uri(httpsUrl).retrieve().toEntity(String.class)) .withCauseInstanceOf(SSLHandshakeException.class); } diff --git a/module/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/livereload/LiveReloadServerTests.java b/module/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/livereload/LiveReloadServerTests.java index b32d6f3a868..402b0893d67 100644 --- a/module/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/livereload/LiveReloadServerTests.java +++ b/module/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/livereload/LiveReloadServerTests.java @@ -55,7 +55,7 @@ import org.junit.jupiter.api.Test; import org.springframework.core.task.AsyncTaskExecutor; import org.springframework.http.HttpHeaders; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestClient; import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.PingMessage; import org.springframework.web.socket.PongMessage; @@ -107,9 +107,9 @@ class LiveReloadServerTests { @Test @Disabled void servesLivereloadJs() throws Exception { - RestTemplate template = new RestTemplate(); + RestClient restClient = RestClient.create(); URI uri = new URI("http://localhost:" + this.port + "/livereload.js"); - String script = template.getForObject(uri, String.class); + String script = restClient.get().uri(uri).retrieve().body(String.class); assertThat(script).contains("livereload.com/protocols/official-7"); } diff --git a/module/spring-boot-jersey/src/test/java/org/springframework/boot/jersey/autoconfigure/metrics/JerseyServerMetricsAutoConfigurationTests.java b/module/spring-boot-jersey/src/test/java/org/springframework/boot/jersey/autoconfigure/metrics/JerseyServerMetricsAutoConfigurationTests.java index bb529516b91..e632b6978ee 100644 --- a/module/spring-boot-jersey/src/test/java/org/springframework/boot/jersey/autoconfigure/metrics/JerseyServerMetricsAutoConfigurationTests.java +++ b/module/spring-boot-jersey/src/test/java/org/springframework/boot/jersey/autoconfigure/metrics/JerseyServerMetricsAutoConfigurationTests.java @@ -47,7 +47,7 @@ import org.springframework.boot.tomcat.autoconfigure.servlet.TomcatServletWebSer import org.springframework.boot.web.server.servlet.context.AnnotationConfigServletWebServerApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestClient; import static org.assertj.core.api.Assertions.assertThat; @@ -112,8 +112,8 @@ class JerseyServerMetricsAutoConfigurationTests { int port = context.getSourceApplicationContext(AnnotationConfigServletWebServerApplicationContext.class) .getWebServer() .getPort(); - RestTemplate restTemplate = new RestTemplate(); - restTemplate.getForEntity(URI.create("http://localhost:" + port + "/users/3"), String.class); + RestClient restClient = RestClient.create(); + restClient.get().uri(URI.create("http://localhost:" + port + "/users/3")).retrieve().toEntity(String.class); } @Configuration(proxyBeanMethods = false) diff --git a/module/spring-boot-servlet/src/test/java/org/springframework/boot/servlet/autoconfigure/MultipartAutoConfigurationTests.java b/module/spring-boot-servlet/src/test/java/org/springframework/boot/servlet/autoconfigure/MultipartAutoConfigurationTests.java index 5ee766ab607..22650efcb34 100644 --- a/module/spring-boot-servlet/src/test/java/org/springframework/boot/servlet/autoconfigure/MultipartAutoConfigurationTests.java +++ b/module/spring-boot-servlet/src/test/java/org/springframework/boot/servlet/autoconfigure/MultipartAutoConfigurationTests.java @@ -48,7 +48,7 @@ import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestClient; import org.springframework.web.multipart.MultipartResolver; import org.springframework.web.multipart.support.StandardServletMultipartResolver; import org.springframework.web.servlet.DispatcherServlet; @@ -214,11 +214,11 @@ class MultipartAutoConfigurationTests { } private void verifyServletWorks(AnnotationConfigServletWebServerApplicationContext context) { - RestTemplate restTemplate = new RestTemplate(); + RestClient restClient = RestClient.create(); WebServer webServer = context.getWebServer(); assertThat(webServer).isNotNull(); String url = "http://localhost:" + webServer.getPort() + "/"; - assertThat(restTemplate.getForObject(url, String.class)).isEqualTo("Hello"); + assertThat(restClient.get().uri(url).retrieve().body(String.class)).isEqualTo("Hello"); } @Configuration(proxyBeanMethods = false) diff --git a/module/spring-boot-tomcat/src/test/java/org/springframework/boot/tomcat/servlet/TomcatServletWebServerFactoryTests.java b/module/spring-boot-tomcat/src/test/java/org/springframework/boot/tomcat/servlet/TomcatServletWebServerFactoryTests.java index ce992e78d27..06ef35a484c 100644 --- a/module/spring-boot-tomcat/src/test/java/org/springframework/boot/tomcat/servlet/TomcatServletWebServerFactoryTests.java +++ b/module/spring-boot-tomcat/src/test/java/org/springframework/boot/tomcat/servlet/TomcatServletWebServerFactoryTests.java @@ -100,8 +100,6 @@ import org.springframework.boot.web.server.servlet.ConfigurableServletWebServerF import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.support.PropertiesLoaderUtils; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -109,7 +107,7 @@ import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.util.FileSystemUtils; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestClient; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -573,14 +571,15 @@ class TomcatServletWebServerFactoryTests extends AbstractServletWebServerFactory assertThat(servletContext).isNotNull(); File temp = (File) servletContext.getAttribute(ServletContext.TEMPDIR); FileSystemUtils.deleteRecursively(temp); - RestTemplate restTemplate = new RestTemplate(); - HttpHeaders headers = new HttpHeaders(); + RestClient restClient = RestClient.create(); MultiValueMap body = new LinkedMultiValueMap<>(); body.add("file", new ByteArrayResource(new byte[1024 * 1024])); - headers.setContentType(MediaType.MULTIPART_FORM_DATA); - HttpEntity> requestEntity = new HttpEntity<>(body, headers); - ResponseEntity response = restTemplate.postForEntity(getLocalUrl("/upload"), requestEntity, - String.class); + ResponseEntity response = restClient.post() + .uri(getLocalUrl("/upload")) + .contentType(MediaType.MULTIPART_FORM_DATA) + .body(body) + .retrieve() + .toEntity(String.class); assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); } diff --git a/module/spring-boot-web-server/src/testFixtures/java/org/springframework/boot/web/server/autoconfigure/servlet/AbstractServletWebServerAutoConfigurationTests.java b/module/spring-boot-web-server/src/testFixtures/java/org/springframework/boot/web/server/autoconfigure/servlet/AbstractServletWebServerAutoConfigurationTests.java index 0ca5f657f23..27f4b97aad6 100644 --- a/module/spring-boot-web-server/src/testFixtures/java/org/springframework/boot/web/server/autoconfigure/servlet/AbstractServletWebServerAutoConfigurationTests.java +++ b/module/spring-boot-web-server/src/testFixtures/java/org/springframework/boot/web/server/autoconfigure/servlet/AbstractServletWebServerAutoConfigurationTests.java @@ -50,10 +50,10 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.http.HttpStatus; -import org.springframework.http.RequestEntity; import org.springframework.stereotype.Component; import org.springframework.web.client.HttpClientErrorException; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestClient; +import org.springframework.web.client.RestClient.RequestHeadersSpec; import org.springframework.web.filter.ForwardedHeaderFilter; import static org.assertj.core.api.Assertions.assertThat; @@ -166,15 +166,15 @@ public abstract class AbstractServletWebServerAutoConfigurationTests { WebServer webServer = ((WebServerApplicationContext) context.getSourceApplicationContext()) .getWebServer(); int port = webServer.getPort(); - RestTemplate rest = new RestTemplate(); - RequestEntity request = RequestEntity.get("http://localhost:" + port) + RestClient rest = RestClient.create(); + RequestHeadersSpec requestSpec = rest.get() + .uri("http://localhost:" + port) .header("Upgrade", "websocket") .header("Connection", "upgrade") .header("Sec-WebSocket-Version", "13") - .header("Sec-WebSocket-Key", "key") - .build(); + .header("Sec-WebSocket-Key", "key"); assertThatExceptionOfType(HttpClientErrorException.Unauthorized.class) - .isThrownBy(() -> rest.exchange(request, Void.class)); + .isThrownBy(() -> requestSpec.retrieve().toBodilessEntity()); }); } diff --git a/module/spring-boot-websocket/src/test/java/org/springframework/boot/websocket/autoconfigure/servlet/WebSocketMessagingAutoConfigurationTests.java b/module/spring-boot-websocket/src/test/java/org/springframework/boot/websocket/autoconfigure/servlet/WebSocketMessagingAutoConfigurationTests.java index 958c6eabdd2..36ef196a62d 100644 --- a/module/spring-boot-websocket/src/test/java/org/springframework/boot/websocket/autoconfigure/servlet/WebSocketMessagingAutoConfigurationTests.java +++ b/module/spring-boot-websocket/src/test/java/org/springframework/boot/websocket/autoconfigure/servlet/WebSocketMessagingAutoConfigurationTests.java @@ -69,7 +69,7 @@ import org.springframework.messaging.simp.stomp.StompSessionHandler; import org.springframework.messaging.simp.stomp.StompSessionHandlerAdapter; import org.springframework.stereotype.Component; import org.springframework.stereotype.Controller; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestClient; import org.springframework.web.socket.client.standard.StandardWebSocketClient; import org.springframework.web.socket.config.annotation.DelegatingWebSocketMessageBrokerConfiguration; import org.springframework.web.socket.config.annotation.EnableWebSocket; @@ -77,7 +77,7 @@ import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBr import org.springframework.web.socket.config.annotation.StompEndpointRegistry; import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; import org.springframework.web.socket.messaging.WebSocketStompClient; -import org.springframework.web.socket.sockjs.client.RestTemplateXhrTransport; +import org.springframework.web.socket.sockjs.client.RestClientXhrTransport; import org.springframework.web.socket.sockjs.client.SockJsClient; import org.springframework.web.socket.sockjs.client.Transport; import org.springframework.web.socket.sockjs.client.WebSocketTransport; @@ -102,7 +102,7 @@ class WebSocketMessagingAutoConfigurationTests { void setup() { List transports = Arrays.asList( new WebSocketTransport(new StandardWebSocketClient(new WsWebSocketContainer())), - new RestTemplateXhrTransport(new RestTemplate())); + new RestClientXhrTransport(RestClient.create())); this.sockJsClient = new SockJsClient(transports); } diff --git a/smoke-test/spring-boot-smoke-test-session-jdbc/src/test/java/smoketest/session/SampleSessionJdbcApplicationTests.java b/smoke-test/spring-boot-smoke-test-session-jdbc/src/test/java/smoketest/session/SampleSessionJdbcApplicationTests.java index 2f7ca0e2154..bf669174b17 100644 --- a/smoke-test/spring-boot-smoke-test-session-jdbc/src/test/java/smoketest/session/SampleSessionJdbcApplicationTests.java +++ b/smoke-test/spring-boot-smoke-test-session-jdbc/src/test/java/smoketest/session/SampleSessionJdbcApplicationTests.java @@ -18,7 +18,6 @@ package smoketest.session; import java.net.URI; import java.util.Base64; -import java.util.Collections; import java.util.List; import java.util.Map; @@ -26,24 +25,24 @@ import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder; import org.springframework.boot.http.client.HttpClientSettings; import org.springframework.boot.http.client.HttpRedirects; -import org.springframework.boot.restclient.RestTemplateBuilder; import org.springframework.boot.resttestclient.TestRestTemplate; import org.springframework.boot.resttestclient.autoconfigure.AutoConfigureTestRestTemplate; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.server.LocalServerPort; 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.ClientHttpRequestFactory; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestClient; import static org.assertj.core.api.Assertions.assertThat; @@ -62,9 +61,6 @@ class SampleSessionJdbcApplicationTests { private static final HttpClientSettings DONT_FOLLOW_REDIRECTS = HttpClientSettings.defaults() .withRedirects(HttpRedirects.DONT_FOLLOW); - @Autowired - private RestTemplateBuilder restTemplateBuilder; - @Autowired private TestRestTemplate restTemplate; @@ -86,15 +82,18 @@ class SampleSessionJdbcApplicationTests { } private @Nullable String performLogin() { - RestTemplate restTemplate = this.restTemplateBuilder.clientSettings(DONT_FOLLOW_REDIRECTS).build(); - HttpHeaders headers = new HttpHeaders(); - headers.setAccept(Collections.singletonList(MediaType.TEXT_HTML)); - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + ClientHttpRequestFactory requestFactory = ClientHttpRequestFactoryBuilder.detect().build(DONT_FOLLOW_REDIRECTS); + RestClient restClient = RestClient.builder().requestFactory(requestFactory).build(); MultiValueMap form = new LinkedMultiValueMap<>(); form.set("username", "user"); form.set("password", "password"); - ResponseEntity entity = restTemplate.exchange("http://localhost:" + this.port + "/login", - HttpMethod.POST, new HttpEntity<>(form, headers), String.class); + ResponseEntity entity = restClient.post() + .uri("http://localhost:" + this.port + "/login") + .accept(MediaType.TEXT_HTML) + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .body(form) + .retrieve() + .toEntity(String.class); return entity.getHeaders().getFirst("Set-Cookie"); } diff --git a/smoke-test/spring-boot-smoke-test-test/src/main/java/smoketest/test/service/RemoteVehicleDetailsService.java b/smoke-test/spring-boot-smoke-test-test/src/main/java/smoketest/test/service/RemoteVehicleDetailsService.java index ef4939d8e1b..7cf33882462 100644 --- a/smoke-test/spring-boot-smoke-test-test/src/main/java/smoketest/test/service/RemoteVehicleDetailsService.java +++ b/smoke-test/spring-boot-smoke-test-test/src/main/java/smoketest/test/service/RemoteVehicleDetailsService.java @@ -20,12 +20,11 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import smoketest.test.domain.VehicleIdentificationNumber; -import org.springframework.boot.restclient.RestTemplateBuilder; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import org.springframework.util.Assert; import org.springframework.web.client.HttpStatusCodeException; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestClient; /** * {@link VehicleDetailsService} backed by a remote REST service. @@ -37,10 +36,10 @@ public class RemoteVehicleDetailsService implements VehicleDetailsService { private static final Log logger = LogFactory.getLog(RemoteVehicleDetailsService.class); - private final RestTemplate restTemplate; + private final RestClient restClient; - public RemoteVehicleDetailsService(ServiceProperties properties, RestTemplateBuilder restTemplateBuilder) { - this.restTemplate = restTemplateBuilder.baseUri(properties.getVehicleServiceRootUrl()).build(); + public RemoteVehicleDetailsService(ServiceProperties properties, RestClient.Builder restTemplateBuilder) { + this.restClient = restTemplateBuilder.baseUrl(properties.getVehicleServiceRootUrl()).build(); } @Override @@ -49,8 +48,10 @@ public class RemoteVehicleDetailsService implements VehicleDetailsService { Assert.notNull(vin, "'vin' must not be null"); logger.debug("Retrieving vehicle data for: " + vin); try { - VehicleDetails response = this.restTemplate.getForObject("/vehicle/{vin}/details", VehicleDetails.class, - vin); + VehicleDetails response = this.restClient.get() + .uri("/vehicle/{vin}/details", vin) + .retrieve() + .body(VehicleDetails.class); Assert.state(response != null, "'response' must not be null"); return response; } diff --git a/smoke-test/spring-boot-smoke-test-test/src/test/java/smoketest/test/service/RemoteVehicleDetailsServiceTests.java b/smoke-test/spring-boot-smoke-test-test/src/test/java/smoketest/test/service/RemoteVehicleDetailsServiceTests.java index 1f49af1bfb4..8b133391b07 100644 --- a/smoke-test/spring-boot-smoke-test-test/src/test/java/smoketest/test/service/RemoteVehicleDetailsServiceTests.java +++ b/smoke-test/spring-boot-smoke-test-test/src/test/java/smoketest/test/service/RemoteVehicleDetailsServiceTests.java @@ -17,6 +17,7 @@ package smoketest.test.service; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import smoketest.test.domain.VehicleIdentificationNumber; import org.springframework.beans.factory.annotation.Autowired; @@ -25,7 +26,9 @@ import org.springframework.core.io.ClassPathResource; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.test.web.client.MockRestServiceServer; +import org.springframework.test.web.client.RequestMatcher; import org.springframework.web.client.HttpServerErrorException; +import org.springframework.web.client.RestClient; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -39,17 +42,27 @@ import static org.springframework.test.web.client.response.MockRestResponseCreat * Tests for {@link RemoteVehicleDetailsService}. * * @author Phillip Webb + * @author Stephane Nicoll */ -@RestClientTest({ RemoteVehicleDetailsService.class, ServiceProperties.class }) +@RestClientTest +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class RemoteVehicleDetailsServiceTests { private static final String VIN = "00000000000000000"; - @Autowired - private RemoteVehicleDetailsService service; + private static final String BASE_URL = "https://api.example.com"; - @Autowired - private MockRestServiceServer server; + private final RemoteVehicleDetailsService service; + + private final MockRestServiceServer server; + + RemoteVehicleDetailsServiceTests(@Autowired RestClient.Builder restClientBuilder, + @Autowired MockRestServiceServer server) { + ServiceProperties properties = new ServiceProperties(); + properties.setVehicleServiceRootUrl(BASE_URL); + this.service = new RemoteVehicleDetailsService(properties, restClientBuilder); + this.server = server; + } @Test @SuppressWarnings("NullAway") // Test null check @@ -60,7 +73,7 @@ class RemoteVehicleDetailsServiceTests { @Test void getVehicleDetailsWhenResultIsSuccessShouldReturnDetails() { - this.server.expect(requestTo("/vehicle/" + VIN + "/details")) + this.server.expect(prepareRequest("/vehicle/" + VIN + "/details")) .andRespond(withSuccess(getClassPathResource("vehicledetails.json"), MediaType.APPLICATION_JSON)); VehicleDetails details = this.service.getVehicleDetails(new VehicleIdentificationNumber(VIN)); assertThat(details.getMake()).isEqualTo("Honda"); @@ -69,18 +82,22 @@ class RemoteVehicleDetailsServiceTests { @Test void getVehicleDetailsWhenResultIsNotFoundShouldThrowException() { - this.server.expect(requestTo("/vehicle/" + VIN + "/details")).andRespond(withStatus(HttpStatus.NOT_FOUND)); + this.server.expect(prepareRequest("/vehicle/" + VIN + "/details")).andRespond(withStatus(HttpStatus.NOT_FOUND)); assertThatExceptionOfType(VehicleIdentificationNumberNotFoundException.class) .isThrownBy(() -> this.service.getVehicleDetails(new VehicleIdentificationNumber(VIN))); } @Test void getVehicleDetailsWhenResultIServerErrorShouldThrowException() { - this.server.expect(requestTo("/vehicle/" + VIN + "/details")).andRespond(withServerError()); + this.server.expect(prepareRequest("/vehicle/" + VIN + "/details")).andRespond(withServerError()); assertThatExceptionOfType(HttpServerErrorException.class) .isThrownBy(() -> this.service.getVehicleDetails(new VehicleIdentificationNumber(VIN))); } + private static RequestMatcher prepareRequest(String path) { + return requestTo(BASE_URL + path); + } + private ClassPathResource getClassPathResource(String path) { return new ClassPathResource(path, getClass()); }