diff --git a/framework-docs/modules/ROOT/pages/web/webflux-webclient.adoc b/framework-docs/modules/ROOT/pages/web/webflux-webclient.adoc index 01e2cb144a50..a8b3b595ed3e 100644 --- a/framework-docs/modules/ROOT/pages/web/webflux-webclient.adoc +++ b/framework-docs/modules/ROOT/pages/web/webflux-webclient.adoc @@ -2,18 +2,18 @@ = WebClient :page-section-summary-toc: 1 -Spring WebFlux includes a client to perform HTTP requests with. `WebClient` has a -functional, fluent API based on Reactor, see xref:web-reactive.adoc#webflux-reactive-libraries[Reactive Libraries], +Spring WebFlux includes a client to perform HTTP requests. `WebClient` has a +functional, fluent API based on Reactor (see xref:web/webflux-reactive-libraries.adoc[Reactive Libraries]) which enables declarative composition of asynchronous logic without the need to deal with -threads or concurrency. It is fully non-blocking, it supports streaming, and relies on +threads or concurrency. It is fully non-blocking, supports streaming, and relies on the same xref:web/webflux/reactive-spring.adoc#webflux-codecs[codecs] that are also used to encode and decode request and response content on the server side. -`WebClient` needs an HTTP client library to perform requests with. There is built-in +`WebClient` needs an HTTP client library to perform requests. There is built-in support for the following: * {reactor-github-org}/reactor-netty[Reactor Netty] * {java-api}/java.net.http/java/net/http/HttpClient.html[JDK HttpClient] * https://github.com/jetty-project/jetty-reactive-httpclient[Jetty Reactive HttpClient] * https://hc.apache.org/index.html[Apache HttpComponents] -* Others can be plugged via `ClientHttpConnector`. +* Others can be plugged in via `ClientHttpConnector`. diff --git a/framework-docs/modules/ROOT/pages/web/webmvc-client.adoc b/framework-docs/modules/ROOT/pages/web/webmvc-client.adoc index 17bd4bcd567b..e22a36120212 100644 --- a/framework-docs/modules/ROOT/pages/web/webmvc-client.adoc +++ b/framework-docs/modules/ROOT/pages/web/webmvc-client.adoc @@ -15,27 +15,27 @@ See xref:integration/rest-clients.adoc#rest-restclient[`RestClient`] for more de [[webmvc-webclient]] == `WebClient` -`WebClient` is a reactive client to perform HTTP requests with a fluent API. +`WebClient` is a reactive client for making HTTP requests with a fluent API. -See xref:web/webflux-webclient.adoc[WebClient] for more details. +See xref:web/webflux-webclient.adoc[`WebClient`] for more details. [[webmvc-resttemplate]] == `RestTemplate` -`RestTemplate` is a synchronous client to perform HTTP requests. It is the original +`RestTemplate` is a synchronous client for making HTTP requests. It is the original Spring REST client and exposes a simple, template-method API over underlying HTTP client libraries. -See xref:integration/rest-clients.adoc[REST Endpoints] for details. +See xref:integration/rest-clients.adoc#rest-resttemplate[`RestTemplate`] for details. [[webmvc-http-interface]] == HTTP Interface -The Spring Frameworks lets you define an HTTP service as a Java interface with HTTP +The Spring Framework lets you define an HTTP service as a Java interface with HTTP exchange methods. You can then generate a proxy that implements this interface and performs the exchanges. This helps to simplify HTTP remote access and provides additional -flexibility for to choose an API style such as synchronous or reactive. +flexibility for choosing an API style such as synchronous or reactive. -See xref:integration/rest-clients.adoc#rest-http-interface[REST Endpoints] for details. +See xref:integration/rest-clients.adoc#rest-http-interface[HTTP Interface] for details. diff --git a/spring-core/src/test/java/org/springframework/util/StringUtilsTests.java b/spring-core/src/test/java/org/springframework/util/StringUtilsTests.java index bdfd1ca82b52..0d9376bf74f4 100644 --- a/spring-core/src/test/java/org/springframework/util/StringUtilsTests.java +++ b/spring-core/src/test/java/org/springframework/util/StringUtilsTests.java @@ -794,6 +794,25 @@ void collectionToDelimitedStringWithNullValuesShouldNotFail() { assertThat(StringUtils.collectionToCommaDelimitedString(Collections.singletonList(null))).isEqualTo("null"); } + @Test + void applyRelativePath() { + // Basic combination + assertThat(StringUtils.applyRelativePath("mypath/myfile", "otherfile")).isEqualTo("mypath/otherfile"); + // Relative path starts with slash + assertThat(StringUtils.applyRelativePath("mypath/myfile", "/otherfile")).isEqualTo("mypath/otherfile"); + // Includes root path + assertThat(StringUtils.applyRelativePath("/mypath/myfile", "otherfile")).isEqualTo("/mypath/otherfile"); + assertThat(StringUtils.applyRelativePath("/mypath/myfile", "/otherfile")).isEqualTo("/mypath/otherfile"); + // When base path has no slash + assertThat(StringUtils.applyRelativePath("myfile", "otherfile")).isEqualTo("otherfile"); + // Keep parent directory token as-is + assertThat(StringUtils.applyRelativePath("mypath/myfile", "../otherfile")).isEqualTo("mypath/../otherfile"); + // Base path ends with slash + assertThat(StringUtils.applyRelativePath("mypath/", "otherfile")).isEqualTo("mypath/otherfile"); + // Empty relative path + assertThat(StringUtils.applyRelativePath("mypath/myfile", "")).isEqualTo("mypath/"); + } + @Test void truncatePreconditions() { assertThatIllegalArgumentException()