17
17
package org .springframework .web .reactive .function ;
18
18
19
19
import java .util .List ;
20
+ import java .util .Map ;
20
21
import java .util .Optional ;
21
22
import java .util .stream .Collectors ;
22
23
27
28
import org .springframework .core .ResolvableType ;
28
29
import org .springframework .core .io .Resource ;
29
30
import org .springframework .core .io .buffer .DataBuffer ;
31
+ import org .springframework .http .HttpEntity ;
30
32
import org .springframework .http .MediaType ;
31
33
import org .springframework .http .ReactiveHttpOutputMessage ;
34
+ import org .springframework .http .client .MultipartBodyBuilder ;
32
35
import org .springframework .http .client .reactive .ClientHttpRequest ;
33
36
import org .springframework .http .codec .HttpMessageWriter ;
34
37
import org .springframework .http .codec .ServerSentEvent ;
@@ -204,14 +207,11 @@ public static <T, S extends Publisher<ServerSentEvent<T>>> BodyInserter<S, Serve
204
207
* @param formData the form data to write to the output message
205
208
* @return a {@code FormInserter} that writes form data
206
209
*/
207
- // Note that the returned FormInserter is parameterized to ClientHttpRequest, not
208
- // ReactiveHttpOutputMessage like other methods, since sending form data only typically happens
209
- // on the client-side
210
210
public static FormInserter <String > fromFormData (MultiValueMap <String , String > formData ) {
211
211
212
212
Assert .notNull (formData , "'formData' must not be null" );
213
213
214
- return DefaultFormInserter . forFormData ().with (formData );
214
+ return new DefaultFormInserter ().with (formData );
215
215
}
216
216
217
217
/**
@@ -222,14 +222,11 @@ public static FormInserter<String> fromFormData(MultiValueMap<String, String> fo
222
222
* @param value the value to add to the form
223
223
* @return a {@code FormInserter} that writes form data
224
224
*/
225
- // Note that the returned FormInserter is parameterized to ClientHttpRequest, not
226
- // ReactiveHttpOutputMessage like other methods, since sending form data only typically happens
227
- // on the client-side
228
225
public static FormInserter <String > fromFormData (String key , String value ) {
229
226
Assert .notNull (key , "'key' must not be null" );
230
227
Assert .notNull (value , "'value' must not be null" );
231
228
232
- return DefaultFormInserter . forFormData ().with (key , value );
229
+ return new DefaultFormInserter ().with (key , value );
233
230
}
234
231
235
232
/**
@@ -251,15 +248,11 @@ public static FormInserter<String> fromFormData(String key, String value) {
251
248
*
252
249
* @param multipartData the form data to write to the output message
253
250
* @return a {@code BodyInserter} that writes multipart data
251
+ * @see MultipartBodyBuilder
254
252
*/
255
- // Note that the returned BodyInserter is parameterized to ClientHttpRequest, not
256
- // ReactiveHttpOutputMessage like other methods, since sending form data only typically happens
257
- // on the client-side
258
- public static <T > FormInserter <T > fromMultipartData (MultiValueMap <String , T > multipartData ) {
259
-
253
+ public static MultipartInserter fromMultipartData (MultiValueMap <String , Object > multipartData ) {
260
254
Assert .notNull (multipartData , "'multipartData' must not be null" );
261
-
262
- return DefaultFormInserter .<T >forMultipartData ().with (multipartData );
255
+ return new DefaultMultipartInserter ().with (multipartData );
263
256
}
264
257
265
258
/**
@@ -271,14 +264,49 @@ public static <T> FormInserter<T> fromMultipartData(MultiValueMap<String, T> mul
271
264
* @return a {@code FormInserter} that can writes the provided multipart
272
265
* data and also allows adding more parts
273
266
*/
274
- // Note that the returned BodyInserter is parameterized to ClientHttpRequest, not
275
- // ReactiveHttpOutputMessage like other methods, since sending form data only typically happens
276
- // on the client-side
277
- public static <T > FormInserter <T > fromMultipartData (String key , T value ) {
267
+ public static MultipartInserter fromMultipartData (String key , Object value ) {
278
268
Assert .notNull (key , "'key' must not be null" );
279
269
Assert .notNull (value , "'value' must not be null" );
280
270
281
- return DefaultFormInserter .<T >forMultipartData ().with (key , value );
271
+ return new DefaultMultipartInserter ().with (key , value );
272
+ }
273
+
274
+ /**
275
+ * A variant of {@link #fromMultipartData(MultiValueMap)} for adding asynchronous data as a
276
+ * part in-line vs building a {@code MultiValueMap} and passing it in.
277
+ * @param key the part name
278
+ * @param publisher the publisher that forms the part value
279
+ * @param elementClass the class contained in the {@code publisher}
280
+ * @return a {@code FormInserter} that can writes the provided multipart
281
+ * data and also allows adding more parts
282
+ */
283
+ public static <T , P extends Publisher <T >> MultipartInserter fromMultipartAsyncData (String key ,
284
+ P publisher , Class <T > elementClass ) {
285
+
286
+ Assert .notNull (key , "'key' must not be null" );
287
+ Assert .notNull (publisher , "'publisher' must not be null" );
288
+ Assert .notNull (elementClass , "'elementClass' must not be null" );
289
+
290
+ return new DefaultMultipartInserter ().withPublisher (key , publisher , elementClass );
291
+ }
292
+
293
+ /**
294
+ * A variant of {@link #fromMultipartData(MultiValueMap)} for adding asynchronous data as a
295
+ * part in-line vs building a {@code MultiValueMap} and passing it in.
296
+ * @param key the part name
297
+ * @param publisher the publisher that forms the part value
298
+ * @param typeReference the type contained in the {@code publisher}
299
+ * @return a {@code FormInserter} that can writes the provided multipart
300
+ * data and also allows adding more parts
301
+ */
302
+ public static <T , P extends Publisher <T >> MultipartInserter fromMultipartAsyncData (String key ,
303
+ P publisher , ParameterizedTypeReference <T > typeReference ) {
304
+
305
+ Assert .notNull (key , "'key' must not be null" );
306
+ Assert .notNull (publisher , "'publisher' must not be null" );
307
+ Assert .notNull (typeReference , "'typeReference' must not be null" );
308
+
309
+ return new DefaultMultipartInserter ().withPublisher (key , publisher , typeReference );
282
310
}
283
311
284
312
/**
@@ -350,6 +378,8 @@ private static <T> HttpMessageWriter<T> cast(HttpMessageWriter<?> messageWriter)
350
378
* Sub-interface of {@link BodyInserter} that allows for additional (multipart) form data to be
351
379
* added.
352
380
*/
381
+ // Note that FormInserter is parameterized to ClientHttpRequest, not ReactiveHttpOutputMessage
382
+ // like other return values methods, since sending form data only typically happens on the client-side
353
383
public interface FormInserter <T > extends
354
384
BodyInserter <MultiValueMap <String , T >, ClientHttpRequest > {
355
385
@@ -370,45 +400,113 @@ public interface FormInserter<T> extends
370
400
371
401
}
372
402
373
- private static class DefaultFormInserter <T > implements FormInserter <T > {
374
403
375
- private final MultiValueMap <String , T > data = new LinkedMultiValueMap <>();
404
+ /**
405
+ * Extension of {@link FormInserter} that has methods for adding asynchronous part data.
406
+ */
407
+ public interface MultipartInserter extends FormInserter <Object > {
408
+
409
+ /**
410
+ * Adds the specified publisher as a part.
411
+ *
412
+ * @param key the key to be added
413
+ * @param publisher the publisher to be added as value
414
+ * @param elementClass the class of elements contained in {@code publisher}
415
+ * @return this inserter
416
+ */
417
+ <T , P extends Publisher <T >> MultipartInserter withPublisher (String key , P publisher ,
418
+ Class <T > elementClass );
419
+
420
+ /**
421
+ * Adds the specified publisher as a part.
422
+ *
423
+ * @param key the key to be added
424
+ * @param publisher the publisher to be added as value
425
+ * @param typeReference the type of elements contained in {@code publisher}
426
+ * @return this inserter
427
+ */
428
+ <T , P extends Publisher <T >> MultipartInserter withPublisher (String key , P publisher ,
429
+ ParameterizedTypeReference <T > typeReference );
430
+
431
+ }
432
+
433
+
434
+ private static class DefaultFormInserter implements FormInserter <String > {
435
+
436
+ private final MultiValueMap <String , String > data = new LinkedMultiValueMap <>();
437
+
438
+ public DefaultFormInserter () {
439
+ }
440
+
441
+ @ Override
442
+ public FormInserter <String > with (String key , @ Nullable String value ) {
443
+ this .data .add (key , value );
444
+ return this ;
445
+ }
446
+
447
+ @ Override
448
+ public FormInserter <String > with (MultiValueMap <String , String > values ) {
449
+ this .data .addAll (values );
450
+ return this ;
451
+ }
452
+
453
+ @ Override
454
+ public Mono <Void > insert (ClientHttpRequest outputMessage , Context context ) {
455
+ HttpMessageWriter <MultiValueMap <String , String >> messageWriter =
456
+ findMessageWriter (context , FORM_TYPE , MediaType .APPLICATION_FORM_URLENCODED );
457
+ return messageWriter .write (Mono .just (this .data ), FORM_TYPE ,
458
+ MediaType .APPLICATION_FORM_URLENCODED ,
459
+ outputMessage , context .hints ());
460
+ }
461
+ }
376
462
377
- private final ResolvableType type ;
378
463
379
- private final MediaType mediaType ;
464
+ private static class DefaultMultipartInserter implements MultipartInserter {
380
465
466
+ private final MultipartBodyBuilder builder = new MultipartBodyBuilder ();
381
467
382
- private DefaultFormInserter (ResolvableType type , MediaType mediaType ) {
383
- this .type = type ;
384
- this .mediaType = mediaType ;
468
+ public DefaultMultipartInserter () {
385
469
}
386
470
387
- public static FormInserter <String > forFormData () {
388
- return new DefaultFormInserter <>(FORM_TYPE , MediaType .APPLICATION_FORM_URLENCODED );
471
+ @ Override
472
+ public MultipartInserter with (String key , @ Nullable Object value ) {
473
+ Assert .notNull (value , "'value' must not be null" );
474
+ this .builder .part (key , value );
475
+ return this ;
389
476
}
390
477
391
- public static <T > FormInserter <T > forMultipartData () {
392
- return new DefaultFormInserter <>(MULTIPART_VALUE_TYPE , MediaType .MULTIPART_FORM_DATA );
478
+ @ Override
479
+ public MultipartInserter with (MultiValueMap <String , Object > values ) {
480
+ Assert .notNull (values , "'values' must not be null" );
481
+ for (Map .Entry <String , List <Object >> entry : values .entrySet ()) {
482
+ this .builder .part (entry .getKey (), entry .getValue ());
483
+ }
484
+ return this ;
393
485
}
394
486
395
487
@ Override
396
- public FormInserter <T > with (String key , @ Nullable T value ) {
397
- this .data .add (key , value );
488
+ public <T , P extends Publisher <T >> MultipartInserter withPublisher (String key ,
489
+ P publisher , Class <T > elementClass ) {
490
+
491
+ this .builder .asyncPart (key , publisher , elementClass );
398
492
return this ;
399
493
}
400
494
401
495
@ Override
402
- public FormInserter <T > with (MultiValueMap <String , T > values ) {
403
- this .data .addAll (values );
496
+ public <T , P extends Publisher <T >> MultipartInserter withPublisher (String key ,
497
+ P publisher , ParameterizedTypeReference <T > typeReference ) {
498
+
499
+ this .builder .asyncPart (key , publisher , typeReference );
404
500
return this ;
405
501
}
406
502
407
503
@ Override
408
504
public Mono <Void > insert (ClientHttpRequest outputMessage , Context context ) {
409
- HttpMessageWriter <MultiValueMap <String , T >> messageWriter =
410
- findMessageWriter (context , this .type , this .mediaType );
411
- return messageWriter .write (Mono .just (this .data ), this .type , this .mediaType ,
505
+ HttpMessageWriter <MultiValueMap <String , HttpEntity <?>>> messageWriter =
506
+ findMessageWriter (context , MULTIPART_VALUE_TYPE , MediaType .MULTIPART_FORM_DATA );
507
+ MultiValueMap <String , HttpEntity <?>> body = this .builder .build ();
508
+ return messageWriter .write (Mono .just (body ), MULTIPART_VALUE_TYPE ,
509
+ MediaType .MULTIPART_FORM_DATA ,
412
510
outputMessage , context .hints ());
413
511
}
414
512
}
0 commit comments