From 5e0a026e6928c4cdfc34769bc6734f4876dd803b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Mon, 25 Mar 2019 15:27:45 +0100 Subject: [PATCH 1/2] RequestParamMethodArgumentResolver does not create empty params from Optional arguments --- .../method/annotation/RequestParamMethodArgumentResolver.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spring-web/src/main/java/org/springframework/web/method/annotation/RequestParamMethodArgumentResolver.java b/spring-web/src/main/java/org/springframework/web/method/annotation/RequestParamMethodArgumentResolver.java index 5682353a8d91..fd1ac5d95176 100644 --- a/spring-web/src/main/java/org/springframework/web/method/annotation/RequestParamMethodArgumentResolver.java +++ b/spring-web/src/main/java/org/springframework/web/method/annotation/RequestParamMethodArgumentResolver.java @@ -20,6 +20,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Optional; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.Part; @@ -224,6 +225,9 @@ public void contributeMethodArgument(MethodParameter parameter, @Nullable Object } builder.queryParam(name); } + else if (value instanceof Optional && !((Optional) value).isPresent()) { + return; + } else if (value instanceof Collection) { for (Object element : (Collection) value) { element = formatUriValue(conversionService, TypeDescriptor.nested(parameter, 1), element); From 5f2c1331d4c276171762e177519f8c8021ccf79a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Mon, 25 Mar 2019 15:30:07 +0100 Subject: [PATCH 2/2] OptionalUnwrappingConverter --- .../support/DefaultConversionService.java | 1 + .../support/OptionalUnwrappingConverter.java | 64 +++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 spring-core/src/main/java/org/springframework/core/convert/support/OptionalUnwrappingConverter.java diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/DefaultConversionService.java b/spring-core/src/main/java/org/springframework/core/convert/support/DefaultConversionService.java index dfd95ca3788c..72713edca2bf 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/support/DefaultConversionService.java +++ b/spring-core/src/main/java/org/springframework/core/convert/support/DefaultConversionService.java @@ -97,6 +97,7 @@ public static void addDefaultConverters(ConverterRegistry converterRegistry) { converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry)); converterRegistry.addConverter(new FallbackObjectToStringConverter()); converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry)); + converterRegistry.addConverter(new OptionalUnwrappingConverter((ConversionService) converterRegistry)); } /** diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/OptionalUnwrappingConverter.java b/spring-core/src/main/java/org/springframework/core/convert/support/OptionalUnwrappingConverter.java new file mode 100644 index 000000000000..6535e6d7cc20 --- /dev/null +++ b/spring-core/src/main/java/org/springframework/core/convert/support/OptionalUnwrappingConverter.java @@ -0,0 +1,64 @@ +package org.springframework.core.convert.support; + +import org.springframework.core.convert.ConversionService; +import org.springframework.core.convert.TypeDescriptor; +import org.springframework.core.convert.converter.ConditionalGenericConverter; + +import java.util.*; + +final class OptionalUnwrappingConverter implements ConditionalGenericConverter { + + private final ConversionService conversionService; + + public OptionalUnwrappingConverter(final ConversionService conversionService) + { + this.conversionService = conversionService; + } + + @Override + public Set getConvertibleTypes() + { + return null; + } + + @Override + public boolean matches(final TypeDescriptor sourceType, final TypeDescriptor targetType) + { + if (!Optional.class.isAssignableFrom(sourceType.getObjectType())) { + return false; + } + + if (!sourceType.getResolvableType().hasGenerics()) { + return false; + } + + return conversionService.canConvert(new GenericTypeDescriptor(sourceType), targetType); + } + + @Override + public Object convert(final Object source, final TypeDescriptor sourceType, final TypeDescriptor targetType) + { + if (source == null) { + return null; + } + + Optional optionalSource = Optional.class.cast(source); + if (!optionalSource.isPresent()) { + return null; + } + + return conversionService.convert(optionalSource.get(), new GenericTypeDescriptor(sourceType), targetType); + } + + @SuppressWarnings("serial") + private static class GenericTypeDescriptor extends TypeDescriptor + { + + GenericTypeDescriptor(final TypeDescriptor typeDescriptor) + { + super(typeDescriptor.getResolvableType().getGeneric(), null, typeDescriptor.getAnnotations()); + } + + } + +}