Skip to content

Commit 14f02d7

Browse files
committed
Update reference with examples of multipart requests
Issue: SPR-16118
1 parent 4ec60f0 commit 14f02d7

File tree

9 files changed

+242
-130
lines changed

9 files changed

+242
-130
lines changed

spring-test/src/main/java/org/springframework/test/web/reactive/server/WebTestClient.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,17 @@ interface RequestBodySpec extends RequestHeadersSpec<RequestBodySpec> {
615615
/**
616616
* Set the body of the request to the given synchronous {@code Object} and
617617
* perform the request.
618+
* <p>This method is a convenient shortcut for:
619+
* <pre class="code">
620+
* .body(BodyInserters.fromObject(object))
621+
* </pre>
622+
* <p>The body can be a
623+
* {@link org.springframework.util.MultiValueMap MultiValueMap} to create
624+
* a multipart request. The values in the {@code MultiValueMap} can be
625+
* any Object representing the body of the part, or an
626+
* {@link org.springframework.http.HttpEntity HttpEntity} representing a
627+
* part with body and headers. The {@code MultiValueMap} can be built
628+
* conveniently using
618629
* @param body the {@code Object} to write to the request
619630
* @return a {@code Mono} with the response
620631
*/

spring-web/src/main/java/org/springframework/web/client/RestOperations.java

Lines changed: 63 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -149,9 +149,13 @@ <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String
149149
* the {@code Location} header. This header typically indicates where the new resource is stored.
150150
* <p>URI Template variables are expanded using the given URI variables, if any.
151151
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
152-
* add additional HTTP headers to the request. The body of the entity, or {@code request} itself,
153-
* can be a {@link org.springframework.http.client.MultipartBodyBuilder MultiValueMap} to
154-
* simulate a multipart from submission.
152+
* add additional HTTP headers to the request.
153+
* <p>The body of the entity, or {@code request} itself, can be a
154+
* {@link org.springframework.util.MultiValueMap MultiValueMap} to create a multipart request.
155+
* The values in the {@code MultiValueMap} can be any Object representing the body of the part,
156+
* or an {@link org.springframework.http.HttpEntity HttpEntity} representing a part with body
157+
* and headers. The {@code MultiValueMap} can be built conveniently using
158+
* {@link org.springframework.http.client.MultipartBodyBuilder MultipartBodyBuilder}.
155159
* @param url the URL
156160
* @param request the Object to be POSTed (may be {@code null})
157161
* @param uriVariables the variables to expand the template
@@ -166,9 +170,13 @@ <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String
166170
* the {@code Location} header. This header typically indicates where the new resource is stored.
167171
* <p>URI Template variables are expanded using the given map.
168172
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
169-
* add additional HTTP headers to the request. The body of the entity, or {@code request} itself,
170-
* can be a {@link org.springframework.http.client.MultipartBodyBuilder MultiValueMap} to
171-
* simulate a multipart from submission.
173+
* add additional HTTP headers to the request
174+
* <p>The body of the entity, or {@code request} itself, can be a
175+
* {@link org.springframework.util.MultiValueMap MultiValueMap} to create a multipart request.
176+
* The values in the {@code MultiValueMap} can be any Object representing the body of the part,
177+
* or an {@link org.springframework.http.HttpEntity HttpEntity} representing a part with body
178+
* and headers. The {@code MultiValueMap} can be built conveniently using
179+
* {@link org.springframework.http.client.MultipartBodyBuilder MultipartBodyBuilder}.
172180
* @param url the URL
173181
* @param request the Object to be POSTed (may be {@code null})
174182
* @param uriVariables the variables to expand the template
@@ -183,9 +191,13 @@ URI postForLocation(String url, @Nullable Object request, Map<String, ?> uriVari
183191
* Create a new resource by POSTing the given object to the URL, and returns the value of the
184192
* {@code Location} header. This header typically indicates where the new resource is stored.
185193
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
186-
* add additional HTTP headers to the request. The body of the entity, or {@code request} itself,
187-
* can be a {@link org.springframework.http.client.MultipartBodyBuilder MultiValueMap} to
188-
* simulate a multipart from submission.
194+
* add additional HTTP headers to the request.
195+
* <p>The body of the entity, or {@code request} itself, can be a
196+
* {@link org.springframework.util.MultiValueMap MultiValueMap} to create a multipart request.
197+
* The values in the {@code MultiValueMap} can be any Object representing the body of the part,
198+
* or an {@link org.springframework.http.HttpEntity HttpEntity} representing a part with body
199+
* and headers. The {@code MultiValueMap} can be built conveniently using
200+
* {@link org.springframework.http.client.MultipartBodyBuilder MultipartBodyBuilder}.
189201
* @param url the URL
190202
* @param request the Object to be POSTed (may be {@code null})
191203
* @return the value for the {@code Location} header
@@ -199,9 +211,13 @@ URI postForLocation(String url, @Nullable Object request, Map<String, ?> uriVari
199211
* and returns the representation found in the response.
200212
* <p>URI Template variables are expanded using the given URI variables, if any.
201213
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
202-
* add additional HTTP headers to the request. The body of the entity, or {@code request} itself,
203-
* can be a {@link org.springframework.http.client.MultipartBodyBuilder MultiValueMap} to
204-
* simulate a multipart from submission.
214+
* add additional HTTP headers to the request.
215+
* <p>The body of the entity, or {@code request} itself, can be a
216+
* {@link org.springframework.util.MultiValueMap MultiValueMap} to create a multipart request.
217+
* The values in the {@code MultiValueMap} can be any Object representing the body of the part,
218+
* or an {@link org.springframework.http.HttpEntity HttpEntity} representing a part with body
219+
* and headers. The {@code MultiValueMap} can be built conveniently using
220+
* {@link org.springframework.http.client.MultipartBodyBuilder MultipartBodyBuilder}.
205221
* @param url the URL
206222
* @param request the Object to be POSTed (may be {@code null})
207223
* @param responseType the type of the return value
@@ -218,9 +234,13 @@ <T> T postForObject(String url, @Nullable Object request, Class<T> responseType,
218234
* and returns the representation found in the response.
219235
* <p>URI Template variables are expanded using the given map.
220236
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
221-
* add additional HTTP headers to the request. The body of the entity, or {@code request} itself,
222-
* can be a {@link org.springframework.http.client.MultipartBodyBuilder MultiValueMap} to
223-
* simulate a multipart from submission.
237+
* add additional HTTP headers to the request.
238+
* <p>The body of the entity, or {@code request} itself, can be a
239+
* {@link org.springframework.util.MultiValueMap MultiValueMap} to create a multipart request.
240+
* The values in the {@code MultiValueMap} can be any Object representing the body of the part,
241+
* or an {@link org.springframework.http.HttpEntity HttpEntity} representing a part with body
242+
* and headers. The {@code MultiValueMap} can be built conveniently using
243+
* {@link org.springframework.http.client.MultipartBodyBuilder MultipartBodyBuilder}.
224244
* @param url the URL
225245
* @param request the Object to be POSTed (may be {@code null})
226246
* @param responseType the type of the return value
@@ -236,9 +256,13 @@ <T> T postForObject(String url, @Nullable Object request, Class<T> responseType,
236256
* Create a new resource by POSTing the given object to the URL,
237257
* and returns the representation found in the response.
238258
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
239-
* add additional HTTP headers to the request. The body of the entity, or {@code request} itself,
240-
* can be a {@link org.springframework.http.client.MultipartBodyBuilder MultiValueMap} to
241-
* simulate a multipart from submission.
259+
* add additional HTTP headers to the request.
260+
* <p>The body of the entity, or {@code request} itself, can be a
261+
* {@link org.springframework.util.MultiValueMap MultiValueMap} to create a multipart request.
262+
* The values in the {@code MultiValueMap} can be any Object representing the body of the part,
263+
* or an {@link org.springframework.http.HttpEntity HttpEntity} representing a part with body
264+
* and headers. The {@code MultiValueMap} can be built conveniently using
265+
* {@link org.springframework.http.client.MultipartBodyBuilder MultipartBodyBuilder}.
242266
* @param url the URL
243267
* @param request the Object to be POSTed (may be {@code null})
244268
* @param responseType the type of the return value
@@ -253,9 +277,13 @@ <T> T postForObject(String url, @Nullable Object request, Class<T> responseType,
253277
* and returns the response as {@link ResponseEntity}.
254278
* <p>URI Template variables are expanded using the given URI variables, if any.
255279
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
256-
* add additional HTTP headers to the request. The body of the entity, or {@code request} itself,
257-
* can be a {@link org.springframework.http.client.MultipartBodyBuilder MultiValueMap} to
258-
* simulate a multipart from submission.
280+
* add additional HTTP headers to the request.
281+
* <p>The body of the entity, or {@code request} itself, can be a
282+
* {@link org.springframework.util.MultiValueMap MultiValueMap} to create a multipart request.
283+
* The values in the {@code MultiValueMap} can be any Object representing the body of the part,
284+
* or an {@link org.springframework.http.HttpEntity HttpEntity} representing a part with body
285+
* and headers. The {@code MultiValueMap} can be built conveniently using
286+
* {@link org.springframework.http.client.MultipartBodyBuilder MultipartBodyBuilder}.
259287
* @param url the URL
260288
* @param request the Object to be POSTed (may be {@code null})
261289
* @param uriVariables the variables to expand the template
@@ -271,9 +299,13 @@ <T> ResponseEntity<T> postForEntity(String url, @Nullable Object request, Class<
271299
* and returns the response as {@link HttpEntity}.
272300
* <p>URI Template variables are expanded using the given map.
273301
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
274-
* add additional HTTP headers to the request. The body of the entity, or {@code request} itself,
275-
* can be a {@link org.springframework.http.client.MultipartBodyBuilder MultiValueMap} to
276-
* simulate a multipart from submission.
302+
* add additional HTTP headers to the request.
303+
* <p>The body of the entity, or {@code request} itself, can be a
304+
* {@link org.springframework.util.MultiValueMap MultiValueMap} to create a multipart request.
305+
* The values in the {@code MultiValueMap} can be any Object representing the body of the part,
306+
* or an {@link org.springframework.http.HttpEntity HttpEntity} representing a part with body
307+
* and headers. The {@code MultiValueMap} can be built conveniently using
308+
* {@link org.springframework.http.client.MultipartBodyBuilder MultipartBodyBuilder}.
277309
* @param url the URL
278310
* @param request the Object to be POSTed (may be {@code null})
279311
* @param uriVariables the variables to expand the template
@@ -288,9 +320,13 @@ <T> ResponseEntity<T> postForEntity(String url, @Nullable Object request, Class<
288320
* Create a new resource by POSTing the given object to the URL,
289321
* and returns the response as {@link ResponseEntity}.
290322
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
291-
* add additional HTTP headers to the request. The body of the entity, or {@code request} itself,
292-
* can be a {@link org.springframework.http.client.MultipartBodyBuilder MultiValueMap} to
293-
* simulate a multipart from submission.
323+
* add additional HTTP headers to the request.
324+
* <p>The body of the entity, or {@code request} itself, can be a
325+
* {@link org.springframework.util.MultiValueMap MultiValueMap} to create a multipart request.
326+
* The values in the {@code MultiValueMap} can be any Object representing the body of the part,
327+
* or an {@link org.springframework.http.HttpEntity HttpEntity} representing a part with body
328+
* and headers. The {@code MultiValueMap} can be built conveniently using
329+
* {@link org.springframework.http.client.MultipartBodyBuilder MultipartBodyBuilder}.
294330
* @param url the URL
295331
* @param request the Object to be POSTed (may be {@code null})
296332
* @return the converted object

spring-webflux/src/main/java/org/springframework/web/reactive/function/BodyInserters.java

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -220,10 +220,13 @@ public static FormInserter<String> fromFormData(String key, String value) {
220220

221221
/**
222222
* Return a {@code FormInserter} that writes the given {@code MultiValueMap}
223-
* as multipart data. The {@code multipartData} parameter can conveniently be built using the
224-
* {@link org.springframework.http.client.MultipartBodyBuilder MultipartBodyBuilder}.
225-
* Note that the returned inserter allows for additional entries to be added
226-
* via {@link FormInserter#with(String, Object)}.
223+
* as multipart data. The values in the {@code MultiValueMap} can be any
224+
* Object representing the body of the part, or an
225+
* {@link org.springframework.http.HttpEntity HttpEntity} representing a part
226+
* with body and headers. The {@code MultiValueMap} can be built conveniently
227+
* using {@link org.springframework.http.client.MultipartBodyBuilder
228+
* MultipartBodyBuilder}. Also the returned inserter allows for additional
229+
* entries to be added via {@link FormInserter#with(String, Object)}.
227230
*
228231
* <p><strong>Note:</strong> you can also use the {@code syncBody(Object)}
229232
* method in the request builders of both the {@code WebClient} and
@@ -245,15 +248,13 @@ public static <T> FormInserter<T> fromMultipartData(MultiValueMap<String, T> mul
245248
}
246249

247250
/**
248-
* Return a {@code FormInserter} that writes the key-value pair as multipart data. The
249-
* {@code multipartData} parameter can conveniently be built using the
250-
* {@link org.springframework.http.client.MultipartBodyBuilder MultipartBodyBuilder}.
251-
* Note that the returned inserter allows for additional entries to be added
252-
* via {@link FormInserter#with(String, Object)}.
253-
* {@link FormInserter#with(String, Object)}.
254-
* @param key the key to add to the form
255-
* @param value the value to add to the form
256-
* @return a {@code FormInserter} that writes multipart data
251+
* A variant of {@link #fromMultipartData(MultiValueMap)} for adding
252+
* parts as name-value pairs in-line vs building a {@code MultiValueMap}
253+
* and passing it in.
254+
* @param key the part name
255+
* @param value the part value, an Object or {@code HttpEntity}
256+
* @return a {@code FormInserter} that can writes the provided multipart
257+
* data and also allows adding more parts
257258
*/
258259
// Note that the returned BodyInserter is parameterized to ClientHttpRequest, not
259260
// ReactiveHttpOutputMessage like other methods, since sending form data only typically happens

spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClient.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -544,9 +544,17 @@ interface RequestBodySpec extends RequestHeadersSpec<RequestBodySpec> {
544544

545545
/**
546546
* Set the body of the request to the given synchronous {@code Object}.
547-
* <p>This method is a convenient shortcut for {@link #body(BodyInserter)} with a
548-
* {@linkplain org.springframework.web.reactive.function.BodyInserters#fromObject
549-
* Object body inserter}.
547+
* <p>This method is a convenient shortcut for:
548+
* <pre class="code">
549+
* .body(BodyInserters.fromObject(object))
550+
* </pre>
551+
* <p>The body can be a
552+
* {@link org.springframework.util.MultiValueMap MultiValueMap} to create
553+
* a multipart request. The values in the {@code MultiValueMap} can be
554+
* any Object representing the body of the part, or an
555+
* {@link org.springframework.http.HttpEntity HttpEntity} representing a
556+
* part with body and headers. The {@code MultiValueMap} can be built
557+
* conveniently using
550558
* @param body the {@code Object} to write to the request
551559
* @return this builder
552560
*/

spring-webflux/src/test/java/org/springframework/web/reactive/function/MultipartIntegrationTests.java

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@
2323
import reactor.test.StepVerifier;
2424

2525
import org.springframework.core.io.ClassPathResource;
26+
import org.springframework.core.io.Resource;
2627
import org.springframework.http.HttpEntity;
2728
import org.springframework.http.HttpHeaders;
2829
import org.springframework.http.HttpStatus;
2930
import org.springframework.http.MediaType;
31+
import org.springframework.http.client.MultipartBodyBuilder;
3032
import org.springframework.http.codec.multipart.FilePart;
3133
import org.springframework.http.codec.multipart.FormFieldPart;
3234
import org.springframework.http.codec.multipart.Part;
@@ -75,16 +77,11 @@ public void parts() {
7577
.verifyComplete();
7678
}
7779

78-
private MultiValueMap<String, Object> generateBody() {
79-
HttpHeaders fooHeaders = new HttpHeaders();
80-
fooHeaders.setContentType(MediaType.TEXT_PLAIN);
81-
ClassPathResource fooResource = new ClassPathResource("org/springframework/http/codec/multipart/foo.txt");
82-
HttpEntity<ClassPathResource> fooPart = new HttpEntity<>(fooResource, fooHeaders);
83-
HttpEntity<String> barPart = new HttpEntity<>("bar");
84-
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>();
85-
parts.add("fooPart", fooPart);
86-
parts.add("barPart", barPart);
87-
return parts;
80+
private MultiValueMap<String, HttpEntity<?>> generateBody() {
81+
MultipartBodyBuilder builder = new MultipartBodyBuilder();
82+
builder.part("fooPart", new ClassPathResource("org/springframework/http/codec/multipart/foo.txt"));
83+
builder.part("barPart", "bar");
84+
return builder.build();
8885
}
8986

9087
@Override

spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/MultipartIntegrationTests.java

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,14 @@
3232
import org.springframework.context.annotation.Configuration;
3333
import org.springframework.core.io.ClassPathResource;
3434
import org.springframework.http.HttpEntity;
35-
import org.springframework.http.HttpHeaders;
3635
import org.springframework.http.HttpStatus;
37-
import org.springframework.http.MediaType;
36+
import org.springframework.http.client.MultipartBodyBuilder;
3837
import org.springframework.http.codec.multipart.FilePart;
3938
import org.springframework.http.codec.multipart.FormFieldPart;
4039
import org.springframework.http.codec.multipart.MultipartHttpMessageReader;
4140
import org.springframework.http.codec.multipart.Part;
4241
import org.springframework.http.server.reactive.AbstractHttpHandlerIntegrationTests;
4342
import org.springframework.http.server.reactive.HttpHandler;
44-
import org.springframework.util.LinkedMultiValueMap;
4543
import org.springframework.util.MultiValueMap;
4644
import org.springframework.web.bind.annotation.ModelAttribute;
4745
import org.springframework.web.bind.annotation.PostMapping;
@@ -136,27 +134,13 @@ public void modelAttribute() {
136134
.verifyComplete();
137135
}
138136

139-
140-
private MultiValueMap<String, Object> generateBody() {
141-
142-
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>();
143-
parts.add("fieldPart", "fieldValue");
144-
145-
HttpHeaders headers = new HttpHeaders();
146-
headers.setContentType(MediaType.TEXT_PLAIN);
147-
ClassPathResource resource = new ClassPathResource("foo.txt", MultipartHttpMessageReader.class);
148-
parts.add("fileParts", new HttpEntity<>(resource, headers));
149-
150-
headers = new HttpHeaders();
151-
headers.setContentType(MediaType.IMAGE_PNG);
152-
resource = new ClassPathResource("logo.png", getClass());
153-
parts.add("fileParts", new HttpEntity<>(resource, headers));
154-
155-
headers = new HttpHeaders();
156-
headers.setContentType(MediaType.APPLICATION_JSON);
157-
parts.add("jsonPart", new HttpEntity<>(new Person("Jason"), headers));
158-
159-
return parts;
137+
private MultiValueMap<String, HttpEntity<?>> generateBody() {
138+
MultipartBodyBuilder builder = new MultipartBodyBuilder();
139+
builder.part("fieldPart", "fieldValue");
140+
builder.part("fileParts", new ClassPathResource("foo.txt", MultipartHttpMessageReader.class));
141+
builder.part("fileParts", new ClassPathResource("logo.png", getClass()));
142+
builder.part("jsonPart", new Person("Jason"));
143+
return builder.build();
160144
}
161145

162146

src/docs/asciidoc/testing-webtestclient.adoc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,3 +293,13 @@ from the `reactor-test` module to do that, for example:
293293
.thenCancel()
294294
.verify();
295295
----
296+
297+
298+
[[webtestclient-request-body]]
299+
=== Request body
300+
301+
When it comes to building requests, the `WebTestClient` offers an identical API as the
302+
`WebClient` and the implementation is mostly a simple pass-through. Please refer
303+
to the <<web-reactive.adoc#webflux-client-body,WebClient documentation>> for examples on
304+
how to prepare a request with a body including submitting form data, multipart requests,
305+
and more.

0 commit comments

Comments
 (0)