Fix RestClient API usage in documentation

Closes gh-37137

Signed-off-by: Hyunwoo Jung <hyunwoojung@kakao.com>
This commit is contained in:
Hyunwoo Jung
2026-08-18 18:49:06 +02:00
committed by GitHub
parent 04e5c92162
commit a165094470
5 changed files with 412 additions and 124 deletions
@@ -0,0 +1,144 @@
/*
* Copyright 2002-present 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
*
* https://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.docs.integration.restmessageconversion;
import java.io.IOException;
import java.nio.file.Path;
import com.fasterxml.jackson.annotation.JsonView;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.multipart.FilePart;
import org.springframework.http.converter.multipart.FormFieldPart;
import org.springframework.http.converter.multipart.Part;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestClient;
import static org.springframework.http.MediaType.APPLICATION_JSON;
public class RestClientMessageConversion {
private final RestClient restClient = RestClient.create();
private final Object myBean = new Object();
void useJsonView() {
// tag::jsonview[]
User user = new User("eric", "7!jd#h23");
ResponseEntity<Void> response = this.restClient.post()
.contentType(APPLICATION_JSON)
.body(user)
.hint(JsonView.class.getName(), User.WithoutPasswordView.class)
.retrieve()
.toBodilessEntity();
// end::jsonview[]
}
void sendUrlEncodedForm() {
// tag::urlencodedform[]
MultiValueMap<String, String> form = new LinkedMultiValueMap<>();
form.add("project", "Spring Framework");
form.add("module", "spring-web");
ResponseEntity<Void> response = this.restClient.post()
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.body(form)
.retrieve()
.toBodilessEntity();
// end::urlencodedform[]
}
void sendMultipartData() {
// tag::multipartrequest[]
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>();
parts.add("fieldPart", "fieldValue");
parts.add("filePart", new FileSystemResource("...logo.png"));
parts.add("jsonPart", new Person("Jason"));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_XML);
parts.add("xmlPart", new HttpEntity<>(this.myBean, headers));
ResponseEntity<Void> response = this.restClient.post()
.contentType(MediaType.MULTIPART_FORM_DATA)
.body(parts)
.retrieve()
.toBodilessEntity();
// end::multipartrequest[]
}
void receiveMultipartData() throws IOException {
// tag::multipartresponse[]
MultiValueMap<String, Part> result = this.restClient.get()
.uri("https://example.com/upload")
.accept(MediaType.MULTIPART_FORM_DATA)
.retrieve()
.body(new ParameterizedTypeReference<>() {});
Part field = result.getFirst("fieldPart");
if (field instanceof FormFieldPart formField) {
String fieldValue = formField.value();
}
Part file = result.getFirst("filePart");
if (file instanceof FilePart filePart) {
filePart.transferTo(Path.of("/tmp/" + filePart.filename()));
}
// end::multipartresponse[]
}
public static class User {
private String username;
private String password;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
@JsonView(WithoutPasswordView.class)
public String getUsername() {
return this.username;
}
@JsonView(WithPasswordView.class)
public String getPassword() {
return this.password;
}
public interface WithoutPasswordView {
}
public interface WithPasswordView extends WithoutPasswordView {
}
}
private record Person(String name) {
}
}
@@ -0,0 +1,75 @@
/*
* Copyright 2002-present 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
*
* https://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.docs.integration.restrestclient.create;
import java.io.IOException;
import java.util.Map;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInitializer;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.ApiVersionInserter;
import org.springframework.web.client.RestClient;
public class RestClientCreation {
void createRestClient() {
// tag::snippet[]
RestClient defaultClient = RestClient.create();
RestClient customClient = RestClient.builder()
.requestFactory(new HttpComponentsClientHttpRequestFactory())
.configureMessageConverters(converters -> converters.addCustomConverter(new MyCustomMessageConverter()))
.baseUrl("https://example.com")
.defaultUriVariables(Map.of("variable", "foo"))
.defaultHeader("My-Header", "Foo")
.defaultCookie("My-Cookie", "Bar")
.defaultApiVersion("1.2")
.apiVersionInserter(ApiVersionInserter.useHeader("API-Version"))
.requestInterceptor(new MyCustomInterceptor())
.requestInitializer(new MyCustomInitializer())
.build();
// end::snippet[]
}
private static class MyCustomMessageConverter extends StringHttpMessageConverter {
}
private static class MyCustomInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution) throws IOException {
return execution.execute(request, body);
}
}
private static class MyCustomInitializer implements ClientHttpRequestInitializer {
@Override
public void initialize(ClientHttpRequest request) {
request.getHeaders().add("My-Header", "My-Value");
}
}
}
@@ -0,0 +1,116 @@
/*
* Copyright 2002-present 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
*
* https://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.docs.integration.restmessageconversion
import com.fasterxml.jackson.annotation.JsonView
import org.springframework.core.ParameterizedTypeReference
import org.springframework.core.io.FileSystemResource
import org.springframework.http.HttpEntity
import org.springframework.http.HttpHeaders
import org.springframework.http.MediaType
import org.springframework.http.MediaType.APPLICATION_JSON
import org.springframework.http.ResponseEntity
import org.springframework.http.converter.multipart.FilePart
import org.springframework.http.converter.multipart.FormFieldPart
import org.springframework.http.converter.multipart.Part
import org.springframework.util.LinkedMultiValueMap
import org.springframework.util.MultiValueMap
import org.springframework.web.client.RestClient
import java.nio.file.Path
class RestClientMessageConversion {
private val restClient = RestClient.create()
private val myBean = Any()
fun useJsonView() {
// tag::jsonview[]
val user = User("eric", "7!jd#h23")
val response: ResponseEntity<Void> = restClient.post()
.contentType(APPLICATION_JSON)
.body(user)
.hint(JsonView::class.java.name, User.WithoutPasswordView::class.java)
.retrieve()
.toBodilessEntity()
// end::jsonview[]
}
fun sendUrlEncodedForm() {
// tag::urlencodedform[]
val form: MultiValueMap<String, String> = LinkedMultiValueMap()
form.add("project", "Spring Framework")
form.add("module", "spring-web")
val response: ResponseEntity<Void> = this.restClient.post()
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.body(form)
.retrieve()
.toBodilessEntity()
// end::urlencodedform[]
}
fun sendMultipartData() {
// tag::multipartrequest[]
val parts: MultiValueMap<String, Any> = LinkedMultiValueMap()
parts.add("fieldPart", "fieldValue")
parts.add("filePart", FileSystemResource("...logo.png"))
parts.add("jsonPart", Person("Jason"))
val headers = HttpHeaders()
headers.contentType = MediaType.APPLICATION_XML
parts.add("xmlPart", HttpEntity(myBean, headers))
val response: ResponseEntity<Void> = this.restClient.post()
.contentType(MediaType.MULTIPART_FORM_DATA)
.body(parts)
.retrieve()
.toBodilessEntity()
// end::multipartrequest[]
}
fun receiveMultipartData() {
// tag::multipartresponse[]
val result = this.restClient.get()
.uri("https://example.com/upload")
.accept(MediaType.MULTIPART_FORM_DATA)
.retrieve()
.body(object : ParameterizedTypeReference<MultiValueMap<String, Part>>() {})
val field = result?.getFirst("fieldPart")
if (field is FormFieldPart) {
val fieldValue = field.value()
}
val file = result?.getFirst("filePart")
if (file is FilePart) {
file.transferTo(Path.of("/tmp/" + file.filename()))
}
// end::multipartresponse[]
}
class User(
@JsonView(WithoutPasswordView::class) val username: String,
@JsonView(WithPasswordView::class) val password: String) {
interface WithoutPasswordView
interface WithPasswordView : WithoutPasswordView
}
data class Person(val name: String)
}
@@ -0,0 +1,71 @@
/*
* Copyright 2002-present 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
*
* https://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.docs.integration.restrestclient.create
import org.springframework.http.HttpRequest
import org.springframework.http.client.ClientHttpRequest
import org.springframework.http.client.ClientHttpRequestExecution
import org.springframework.http.client.ClientHttpRequestInitializer
import org.springframework.http.client.ClientHttpRequestInterceptor
import org.springframework.http.client.ClientHttpResponse
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory
import org.springframework.http.converter.StringHttpMessageConverter
import org.springframework.web.client.ApiVersionInserter
import org.springframework.web.client.RestClient
class RestClientCreation {
fun createRestClient() {
// tag::snippet[]
val defaultClient = RestClient.create()
val customClient = RestClient.builder()
.requestFactory(HttpComponentsClientHttpRequestFactory())
.configureMessageConverters { converters -> converters.addCustomConverter(MyCustomMessageConverter()) }
.baseUrl("https://example.com")
.defaultUriVariables(mapOf("variable" to "foo"))
.defaultHeader("My-Header", "Foo")
.defaultCookie("My-Cookie", "Bar")
.defaultApiVersion("1.2")
.apiVersionInserter(ApiVersionInserter.useHeader("API-Version"))
.requestInterceptor(MyCustomInterceptor())
.requestInitializer(MyCustomInitializer())
.build()
// end::snippet[]
}
private class MyCustomMessageConverter : StringHttpMessageConverter()
private class MyCustomInterceptor : ClientHttpRequestInterceptor {
override fun intercept(
request: HttpRequest,
body: ByteArray,
execution: ClientHttpRequestExecution
): ClientHttpResponse {
return execution.execute(request, body)
}
}
private class MyCustomInitializer : ClientHttpRequestInitializer {
override fun initialize(request: ClientHttpRequest) {
request.headers.add("My-Header", "My-Value")
}
}
}