152
152
* @see org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter
153
153
* @see org.springframework.util.MultiValueMap
154
154
*/
155
- public class FormHttpMessageConverter implements HttpMessageConverter <MultiValueMap <String , ?>> {
155
+ public class FormHttpMessageConverter implements HttpMessageConverter <Map <String , ?>> {
156
156
157
157
/** The default charset used by the converter. */
158
158
public static final Charset DEFAULT_CHARSET = StandardCharsets .UTF_8 ;
@@ -295,7 +295,7 @@ public void setMultipartCharset(Charset charset) {
295
295
296
296
@ Override
297
297
public boolean canRead (Class <?> clazz , @ Nullable MediaType mediaType ) {
298
- if (!MultiValueMap .class .isAssignableFrom (clazz )) {
298
+ if (!Map .class .isAssignableFrom (clazz )) {
299
299
return false ;
300
300
}
301
301
if (mediaType == null ) {
@@ -330,7 +330,7 @@ public boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {
330
330
}
331
331
332
332
@ Override
333
- public MultiValueMap <String , String > read (@ Nullable Class <? extends MultiValueMap <String , ?>> clazz ,
333
+ public Map <String , ? > read (@ Nullable Class <? extends Map <String , ?>> clazz ,
334
334
HttpInputMessage inputMessage ) throws IOException , HttpMessageNotReadableException {
335
335
336
336
MediaType contentType = inputMessage .getHeaders ().getContentType ();
@@ -339,41 +339,76 @@ public MultiValueMap<String, String> read(@Nullable Class<? extends MultiValueMa
339
339
String body = StreamUtils .copyToString (inputMessage .getBody (), charset );
340
340
341
341
String [] pairs = StringUtils .tokenizeToStringArray (body , "&" );
342
- MultiValueMap <String , String > result = new LinkedMultiValueMap <>(pairs .length );
342
+ Map <String , String > svResult = null ;
343
+ MultiValueMap <String , String > mvResult = null ;
344
+ if (clazz == null || MultiValueMap .class .isAssignableFrom (clazz )) {
345
+ mvResult = new LinkedMultiValueMap <>(pairs .length );
346
+ }
347
+ else {
348
+ svResult = CollectionUtils .newLinkedHashMap (pairs .length );
349
+ }
343
350
for (String pair : pairs ) {
344
351
int idx = pair .indexOf ('=' );
345
352
if (idx == -1 ) {
346
- result .add (URLDecoder .decode (pair , charset ), null );
353
+ String name = URLDecoder .decode (pair , charset );
354
+ if (mvResult != null ) {
355
+ mvResult .add (name , null );
356
+ }
357
+ else if (svResult != null ) {
358
+ svResult .put (name , null );
359
+ }
347
360
}
348
361
else {
349
362
String name = URLDecoder .decode (pair .substring (0 , idx ), charset );
350
363
String value = URLDecoder .decode (pair .substring (idx + 1 ), charset );
351
- result .add (name , value );
364
+ if (mvResult != null ) {
365
+ mvResult .add (name , value );
366
+ }
367
+ else if (svResult != null ) {
368
+ svResult .put (name , value );
369
+ }
352
370
}
353
371
}
354
- return result ;
372
+ if (mvResult != null ) {
373
+ return mvResult ;
374
+ }
375
+ else if (svResult != null ) {
376
+ return svResult ;
377
+ }
378
+ else {
379
+ throw new IllegalStateException ();
380
+ }
355
381
}
356
382
357
383
@ Override
358
384
@ SuppressWarnings ("unchecked" )
359
- public void write (MultiValueMap <String , ?> map , @ Nullable MediaType contentType , HttpOutputMessage outputMessage )
385
+ public void write (Map <String , ?> map , @ Nullable MediaType contentType , HttpOutputMessage outputMessage )
360
386
throws IOException , HttpMessageNotWritableException {
361
387
362
388
if (isMultipart (map , contentType )) {
363
- writeMultipart ((MultiValueMap <String , Object >) map , contentType , outputMessage );
389
+ writeMultipart ((Map <String , Object >) map , contentType , outputMessage );
364
390
}
365
391
else {
366
- writeForm ((MultiValueMap <String , Object >) map , contentType , outputMessage );
392
+ writeForm ((Map <String , Object >) map , contentType , outputMessage );
367
393
}
368
394
}
369
395
370
396
371
- private boolean isMultipart (MultiValueMap <String , ?> map , @ Nullable MediaType contentType ) {
397
+ private boolean isMultipart (Map <String , ?> map , @ Nullable MediaType contentType ) {
372
398
if (contentType != null ) {
373
399
return contentType .getType ().equalsIgnoreCase ("multipart" );
374
400
}
375
- for (List <?> values : map .values ()) {
376
- for (Object value : values ) {
401
+ if (map instanceof MultiValueMap <?, ?> mvMap ) {
402
+ for (List <?> values : mvMap .values ()) {
403
+ for (Object value : values ) {
404
+ if (value != null && !(value instanceof String )) {
405
+ return true ;
406
+ }
407
+ }
408
+ }
409
+ }
410
+ else {
411
+ for (Object value : map .values ()) {
377
412
if (value != null && !(value instanceof String )) {
378
413
return true ;
379
414
}
@@ -382,7 +417,7 @@ private boolean isMultipart(MultiValueMap<String, ?> map, @Nullable MediaType co
382
417
return false ;
383
418
}
384
419
385
- private void writeForm (MultiValueMap <String , Object > formData , @ Nullable MediaType mediaType ,
420
+ private void writeForm (Map <String , Object > formData , @ Nullable MediaType mediaType ,
386
421
HttpOutputMessage outputMessage ) throws IOException {
387
422
388
423
mediaType = getFormContentType (mediaType );
@@ -430,30 +465,37 @@ protected MediaType getFormContentType(@Nullable MediaType contentType) {
430
465
return contentType ;
431
466
}
432
467
433
- protected String serializeForm (MultiValueMap <String , Object > formData , Charset charset ) {
468
+ protected String serializeForm (Map <String , Object > formData , Charset charset ) {
434
469
StringBuilder builder = new StringBuilder ();
435
- formData .forEach ((name , values ) -> {
470
+ formData .forEach ((name , value ) -> {
436
471
if (name == null ) {
437
- Assert .isTrue (CollectionUtils .isEmpty (values ), () -> "Null name in form data: " + formData );
472
+ Assert .isTrue (value == null || value instanceof List <?> values && values .isEmpty (),
473
+ () -> "Null name in form data: " + formData );
438
474
return ;
439
475
}
440
- values .forEach (value -> {
441
- if (builder .length () != 0 ) {
442
- builder .append ('&' );
443
- }
444
- builder .append (URLEncoder .encode (name , charset ));
445
- if (value != null ) {
446
- builder .append ('=' );
447
- builder .append (URLEncoder .encode (String .valueOf (value ), charset ));
448
- }
449
- });
476
+ if (value instanceof List <?> values ) {
477
+ values .forEach (v -> serializeFormValue (name , value , charset , builder ));
478
+ }
479
+ else {
480
+ serializeFormValue (name , value , charset , builder );
481
+ }
450
482
});
451
-
452
483
return builder .toString ();
453
484
}
454
485
486
+ private static void serializeFormValue (String name , @ Nullable Object value , Charset charset , StringBuilder builder ) {
487
+ if (!builder .isEmpty ()) {
488
+ builder .append ('&' );
489
+ }
490
+ builder .append (URLEncoder .encode (name , charset ));
491
+ if (value != null ) {
492
+ builder .append ('=' );
493
+ builder .append (URLEncoder .encode (String .valueOf (value ), charset ));
494
+ }
495
+ }
496
+
455
497
private void writeMultipart (
456
- MultiValueMap <String , Object > parts , @ Nullable MediaType contentType , HttpOutputMessage outputMessage )
498
+ Map <String , Object > parts , @ Nullable MediaType contentType , HttpOutputMessage outputMessage )
457
499
throws IOException {
458
500
459
501
// If the supplied content type is null, fall back to multipart/form-data.
@@ -500,13 +542,23 @@ private boolean isFilenameCharsetSet() {
500
542
return (this .multipartCharset != null );
501
543
}
502
544
503
- private void writeParts (OutputStream os , MultiValueMap <String , Object > parts , byte [] boundary ) throws IOException {
504
- for (Map .Entry <String , List < Object > > entry : parts .entrySet ()) {
545
+ private void writeParts (OutputStream os , Map <String , Object > parts , byte [] boundary ) throws IOException {
546
+ for (Map .Entry <String , Object > entry : parts .entrySet ()) {
505
547
String name = entry .getKey ();
506
- for (Object part : entry .getValue ()) {
507
- if (part != null ) {
548
+ Object value = entry .getValue ();
549
+ if (value instanceof List <?> values ) {
550
+ for (Object part : values ) {
551
+ if (part != null ) {
552
+ writeBoundary (os , boundary );
553
+ writePart (name , getHttpEntity (part ), os );
554
+ writeNewLine (os );
555
+ }
556
+ }
557
+ }
558
+ else {
559
+ if (value != null ) {
508
560
writeBoundary (os , boundary );
509
- writePart (name , getHttpEntity (part ), os );
561
+ writePart (name , getHttpEntity (value ), os );
510
562
writeNewLine (os );
511
563
}
512
564
}
0 commit comments