diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/reactive/AbstractNamedValueMethodArgumentResolver.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/reactive/AbstractNamedValueMethodArgumentResolver.java index f35a97eae87..ac33fe90257 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/reactive/AbstractNamedValueMethodArgumentResolver.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/reactive/AbstractNamedValueMethodArgumentResolver.java @@ -108,9 +108,13 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements SyncHa if (parameter != nestedParameter || !ClassUtils.isAssignableValue(parameter.getParameterType(), arg)) { arg = this.conversionService.convert(arg, TypeDescriptor.forObject(arg), new TypeDescriptor(parameter)); // Check for null value after conversion of incoming argument value - if (arg == null && namedValueInfo.defaultValue == null && - namedValueInfo.required && !nestedParameter.isOptional()) { - handleMissingValue(namedValueInfo.name, nestedParameter, message); + if (arg == null) { + if (namedValueInfo.defaultValue != null) { + arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue); + } + else if (namedValueInfo.required && !nestedParameter.isOptional()) { + handleMissingValue(namedValueInfo.name, nestedParameter, message); + } } } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/AbstractNamedValueMethodArgumentResolver.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/AbstractNamedValueMethodArgumentResolver.java index 4d6be65ccd2..84f2bf6e36f 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/AbstractNamedValueMethodArgumentResolver.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/AbstractNamedValueMethodArgumentResolver.java @@ -116,9 +116,13 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle if (parameter != nestedParameter || !ClassUtils.isAssignableValue(parameter.getParameterType(), arg)) { arg = this.conversionService.convert(arg, TypeDescriptor.forObject(arg), new TypeDescriptor(parameter)); // Check for null value after conversion of incoming argument value - if (arg == null && namedValueInfo.defaultValue == null && - namedValueInfo.required && !nestedParameter.isOptional()) { - handleMissingValue(namedValueInfo.name, nestedParameter, message); + if (arg == null) { + if (namedValueInfo.defaultValue != null) { + arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue); + } + else if (namedValueInfo.required && !nestedParameter.isOptional()) { + handleMissingValue(namedValueInfo.name, nestedParameter, message); + } } } diff --git a/spring-web/src/main/java/org/springframework/web/method/annotation/AbstractNamedValueMethodArgumentResolver.java b/spring-web/src/main/java/org/springframework/web/method/annotation/AbstractNamedValueMethodArgumentResolver.java index 703362d31e8..8fe5c11b4a3 100644 --- a/spring-web/src/main/java/org/springframework/web/method/annotation/AbstractNamedValueMethodArgumentResolver.java +++ b/spring-web/src/main/java/org/springframework/web/method/annotation/AbstractNamedValueMethodArgumentResolver.java @@ -133,9 +133,13 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle namedValueInfo.name, parameter, ex.getCause()); } // Check for null value after conversion of incoming argument value - if (arg == null && namedValueInfo.defaultValue == null && - namedValueInfo.required && !nestedParameter.isOptional()) { - handleMissingValueAfterConversion(namedValueInfo.name, nestedParameter, webRequest); + if (arg == null) { + if (namedValueInfo.defaultValue != null) { + arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue); + } + else if (namedValueInfo.required && !nestedParameter.isOptional()) { + handleMissingValueAfterConversion(namedValueInfo.name, nestedParameter, webRequest); + } } } diff --git a/spring-web/src/test/java/org/springframework/web/method/annotation/RequestParamMethodArgumentResolverTests.java b/spring-web/src/test/java/org/springframework/web/method/annotation/RequestParamMethodArgumentResolverTests.java index 9594f24c674..2f822820c31 100644 --- a/spring-web/src/test/java/org/springframework/web/method/annotation/RequestParamMethodArgumentResolverTests.java +++ b/spring-web/src/test/java/org/springframework/web/method/annotation/RequestParamMethodArgumentResolverTests.java @@ -462,6 +462,21 @@ public class RequestParamMethodArgumentResolverTests { assertThat(arg).isNull(); } + @Test // gh-29550 + public void missingRequestParamEmptyValueNotRequiredWithDefaultValue() throws Exception { + WebDataBinder binder = new WebRequestDataBinder(null); + binder.registerCustomEditor(String.class, new StringTrimmerEditor(true)); + + WebDataBinderFactory binderFactory = mock(); + given(binderFactory.createBinder(webRequest, null, "name")).willReturn(binder); + + request.addParameter("name", " "); + + MethodParameter param = this.testMethod.annot(requestParam().notRequired("bar")).arg(String.class); + Object arg = resolver.resolveArgument(param, null, webRequest, binderFactory); + assertThat(arg).isEqualTo("bar"); + } + @Test public void resolveSimpleTypeParam() throws Exception { request.setParameter("stringNotAnnot", "plainValue");