50
50
* Implementation of {@link HttpMessageConverter} to read and write 'normal' HTML
51
51
* forms and also to write (but not read) multipart data (e.g. file uploads).
52
52
*
53
- * <p>
54
- * The following table shows an overview of the supported media and class types.
55
- * <table border="1">
56
- * <tr><th>Media type</th><th>Read</th><th>Write</th></tr>
57
- * <tr>
58
- * <td>{@code "application/x-www-form-urlencoded"}</td>
59
- * <td>{@link MultiValueMap MultiValueMap<String, String>}</td>
60
- * <td>{@link Map Map<String, String>}<br>
61
- * {@link MultiValueMap MultiValueMap<String, String>}</td>
62
- * </tr>
63
- * <tr>
64
- * <td>{@code "multipart/form-data"}<br>
65
- * {@code "multipart/mixed"}</td>
66
- * <td>Unsupported</td>
67
- * <td>{@link Map Map<String, Object>}<br>
68
- * {@link MultiValueMap MultiValueMap<String, Object>}</td>
69
- * </tr>
70
- * </table>
53
+ * <p>In other words, this converter can read and write the
54
+ * {@code "application/x-www-form-urlencoded"} media type as
55
+ * {@link MultiValueMap MultiValueMap<String, String>}, and it can also
56
+ * write (but not read) the {@code "multipart/form-data"} and
57
+ * {@code "multipart/mixed"} media types as
58
+ * {@link MultiValueMap MultiValueMap<String, Object>}.
71
59
*
72
60
* <h3>Multipart Data</h3>
73
61
*
167
155
* <p>Some methods in this class were inspired by
168
156
* {@code org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity}.
169
157
*
170
- * <p>As of 6.2, the {@code FormHttpMessageConverter} is parameterized over
171
- * {@code Map<String, ?>} in order to support writing single-value maps.
172
- * Before 6.2, this class was parameterized over {@code MultiValueMap<String, ?>}.
173
- *
174
158
* @author Arjen Poutsma
175
159
* @author Rossen Stoyanchev
176
160
* @author Juergen Hoeller
179
163
* @see org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter
180
164
* @see org.springframework.util.MultiValueMap
181
165
*/
182
- public class FormHttpMessageConverter implements HttpMessageConverter <Map <String , ?>> {
166
+ public class FormHttpMessageConverter implements HttpMessageConverter <MultiValueMap <String , ?>> {
183
167
184
168
/** The default charset used by the converter. */
185
169
public static final Charset DEFAULT_CHARSET = StandardCharsets .UTF_8 ;
@@ -342,7 +326,7 @@ public boolean canRead(Class<?> clazz, @Nullable MediaType mediaType) {
342
326
343
327
@ Override
344
328
public boolean canWrite (Class <?> clazz , @ Nullable MediaType mediaType ) {
345
- if (!Map .class .isAssignableFrom (clazz )) {
329
+ if (!MultiValueMap .class .isAssignableFrom (clazz )) {
346
330
return false ;
347
331
}
348
332
if (mediaType == null || MediaType .ALL .equals (mediaType )) {
@@ -357,7 +341,7 @@ public boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {
357
341
}
358
342
359
343
@ Override
360
- public Map <String , ? > read (@ Nullable Class <? extends Map <String , ?>> clazz ,
344
+ public MultiValueMap <String , String > read (@ Nullable Class <? extends MultiValueMap <String , ?>> clazz ,
361
345
HttpInputMessage inputMessage ) throws IOException , HttpMessageNotReadableException {
362
346
363
347
MediaType contentType = inputMessage .getHeaders ().getContentType ();
@@ -383,38 +367,33 @@ public boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {
383
367
384
368
@ Override
385
369
@ SuppressWarnings ("unchecked" )
386
- public void write (Map <String , ?> map , @ Nullable MediaType contentType , HttpOutputMessage outputMessage )
370
+ public void write (MultiValueMap <String , ?> map , @ Nullable MediaType contentType , HttpOutputMessage outputMessage )
387
371
throws IOException , HttpMessageNotWritableException {
388
372
389
373
if (isMultipart (map , contentType )) {
390
- writeMultipart ((Map <String , Object >) map , contentType , outputMessage );
374
+ writeMultipart ((MultiValueMap <String , Object >) map , contentType , outputMessage );
391
375
}
392
376
else {
393
- writeForm ((Map <String , Object >) map , contentType , outputMessage );
377
+ writeForm ((MultiValueMap <String , Object >) map , contentType , outputMessage );
394
378
}
395
379
}
396
380
397
381
398
- private boolean isMultipart (Map <String , ?> map , @ Nullable MediaType contentType ) {
382
+ private boolean isMultipart (MultiValueMap <String , ?> map , @ Nullable MediaType contentType ) {
399
383
if (contentType != null ) {
400
384
return contentType .getType ().equalsIgnoreCase ("multipart" );
401
385
}
402
- for (Object value : map .values ()) {
403
- if (value instanceof List <?> values ) {
404
- for (Object v : values ) {
405
- if (v != null && !(v instanceof String )) {
406
- return true ;
407
- }
386
+ for (List <?> values : map .values ()) {
387
+ for (Object value : values ) {
388
+ if (value != null && !(value instanceof String )) {
389
+ return true ;
408
390
}
409
391
}
410
- else if (value != null && !(value instanceof String )) {
411
- return true ;
412
- }
413
392
}
414
393
return false ;
415
394
}
416
395
417
- private void writeForm (Map <String , Object > formData , @ Nullable MediaType mediaType ,
396
+ private void writeForm (MultiValueMap <String , Object > formData , @ Nullable MediaType mediaType ,
418
397
HttpOutputMessage outputMessage ) throws IOException {
419
398
420
399
mediaType = getFormContentType (mediaType );
@@ -462,36 +441,30 @@ protected MediaType getFormContentType(@Nullable MediaType contentType) {
462
441
return contentType ;
463
442
}
464
443
465
- protected String serializeForm (Map <String , Object > formData , Charset charset ) {
444
+ protected String serializeForm (MultiValueMap <String , Object > formData , Charset charset ) {
466
445
StringBuilder builder = new StringBuilder ();
467
- formData .forEach ((name , value ) -> {
468
- if (value instanceof List <?> values ) {
446
+ formData .forEach ((name , values ) -> {
469
447
if (name == null ) {
470
448
Assert .isTrue (CollectionUtils .isEmpty (values ), () -> "Null name in form data: " + formData );
471
449
return ;
472
450
}
473
- values .forEach (v -> appendFormValue (builder , name , v , charset ));
474
- }
475
- else {
476
- appendFormValue (builder , name , value , charset );
477
- }
451
+ values .forEach (value -> {
452
+ if (builder .length () != 0 ) {
453
+ builder .append ('&' );
454
+ }
455
+ builder .append (URLEncoder .encode (name , charset ));
456
+ if (value != null ) {
457
+ builder .append ('=' );
458
+ builder .append (URLEncoder .encode (String .valueOf (value ), charset ));
459
+ }
460
+ });
478
461
});
479
- return builder .toString ();
480
- }
481
462
482
- private static void appendFormValue (StringBuilder builder , String name , @ Nullable Object value , Charset charset ) {
483
- if (!builder .isEmpty ()) {
484
- builder .append ('&' );
485
- }
486
- builder .append (URLEncoder .encode (name , charset ));
487
- if (value != null ) {
488
- builder .append ('=' );
489
- builder .append (URLEncoder .encode (String .valueOf (value ), charset ));
490
- }
463
+ return builder .toString ();
491
464
}
492
465
493
466
private void writeMultipart (
494
- Map <String , Object > parts , @ Nullable MediaType contentType , HttpOutputMessage outputMessage )
467
+ MultiValueMap <String , Object > parts , @ Nullable MediaType contentType , HttpOutputMessage outputMessage )
495
468
throws IOException {
496
469
497
470
// If the supplied content type is null, fall back to multipart/form-data.
@@ -538,24 +511,16 @@ private boolean isFilenameCharsetSet() {
538
511
return (this .multipartCharset != null );
539
512
}
540
513
541
- private void writeParts (OutputStream os , Map <String , Object > parts , byte [] boundary ) throws IOException {
542
- for (Map .Entry <String , Object > entry : parts .entrySet ()) {
514
+ private void writeParts (OutputStream os , MultiValueMap <String , Object > parts , byte [] boundary ) throws IOException {
515
+ for (Map .Entry <String , List < Object > > entry : parts .entrySet ()) {
543
516
String name = entry .getKey ();
544
- Object value = entry .getValue ();
545
- if (value instanceof List <?> values ) {
546
- for (Object part : values ) {
547
- if (part != null ) {
548
- writeBoundary (os , boundary );
549
- writePart (name , getHttpEntity (part ), os );
550
- writeNewLine (os );
551
- }
517
+ for (Object part : entry .getValue ()) {
518
+ if (part != null ) {
519
+ writeBoundary (os , boundary );
520
+ writePart (name , getHttpEntity (part ), os );
521
+ writeNewLine (os );
552
522
}
553
523
}
554
- else if (value != null ) {
555
- writeBoundary (os , boundary );
556
- writePart (name , getHttpEntity (value ), os );
557
- writeNewLine (os );
558
- }
559
524
}
560
525
}
561
526
0 commit comments