diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestPartMethodArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestPartMethodArgumentResolver.java index 4fc3994251e..c14f7a9300f 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestPartMethodArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestPartMethodArgumentResolver.java @@ -100,19 +100,12 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageReaderArgu ReactiveAdapter adapter = getAdapterRegistry().getAdapter(parameter.getParameterType()); if (adapter != null) { - // Mono or Flux MethodParameter elementType = parameter.nested(); - if (Part.class.isAssignableFrom(elementType.getNestedParameterType())) { - parts = (adapter.isMultiValue() ? parts : parts.take(1)); - return Mono.just(adapter.fromPublisher(parts)); - } - // We have to decode the content for each part, one at a time - if (adapter.isMultiValue()) { - return Mono.just(decodePartValues(parts, elementType, bindingContext, exchange, isRequired)); - } + return Mono.just(adapter.fromPublisher( + Part.class.isAssignableFrom(elementType.getNestedParameterType()) ? + parts : decodePartValues(parts, elementType, bindingContext, exchange, isRequired))); } - // or Mono return decodePartValues(parts, parameter, bindingContext, exchange, isRequired) .next().cast(Object.class); } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestPartMethodArgumentResolverTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestPartMethodArgumentResolverTests.java index 67fab0358f8..35eac4b1fa9 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestPartMethodArgumentResolverTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestPartMethodArgumentResolverTests.java @@ -60,6 +60,7 @@ import static org.springframework.web.method.MvcAnnotationPredicates.requestPart /** * Unit tests for {@link RequestPartMethodArgumentResolver}. * @author Rossen Stoyanchev + * @author Ilya Lukyanovich */ public class RequestPartMethodArgumentResolverTests { @@ -131,6 +132,15 @@ public class RequestPartMethodArgumentResolverTests { assertThat(actual.get(1).getName()).isEqualTo("James"); } + @Test // gh-23060 + public void listPersonNotRequired() { + MethodParameter param = this.testMethod.annot(requestPart().notRequired()).arg(List.class, Person.class); + MultipartBodyBuilder bodyBuilder = new MultipartBodyBuilder(); + List actual = resolveArgument(param, bodyBuilder); + + assertThat(actual).isEmpty(); + } + @Test public void monoPerson() { MethodParameter param = this.testMethod.annot(requestPart()).arg(Mono.class, Person.class); @@ -141,6 +151,15 @@ public class RequestPartMethodArgumentResolverTests { assertThat(actual.block().getName()).isEqualTo("Jones"); } + @Test // gh-23060 + public void monoPersonNotRequired() { + MethodParameter param = this.testMethod.annot(requestPart().notRequired()).arg(Mono.class, Person.class); + MultipartBodyBuilder bodyBuilder = new MultipartBodyBuilder(); + Mono actual = resolveArgument(param, bodyBuilder); + + assertThat(actual.block()).isNull(); + } + @Test public void fluxPerson() { MethodParameter param = this.testMethod.annot(requestPart()).arg(Flux.class, Person.class); @@ -154,6 +173,15 @@ public class RequestPartMethodArgumentResolverTests { assertThat(persons.get(1).getName()).isEqualTo("James"); } + @Test // gh-23060 + public void fluxPersonNotRequired() { + MethodParameter param = this.testMethod.annot(requestPart().notRequired()).arg(Flux.class, Person.class); + MultipartBodyBuilder bodyBuilder = new MultipartBodyBuilder(); + Flux actual = resolveArgument(param, bodyBuilder); + + assertThat(actual.collectList().block()).isEmpty(); + } + @Test public void part() { MethodParameter param = this.testMethod.annot(requestPart()).arg(Part.class); @@ -177,6 +205,15 @@ public class RequestPartMethodArgumentResolverTests { assertThat(partToUtf8String(actual.get(1))).isEqualTo("{\"name\":\"James\"}"); } + @Test // gh-23060 + public void listPartNotRequired() { + MethodParameter param = this.testMethod.annot(requestPart().notRequired()).arg(List.class, Part.class); + MultipartBodyBuilder bodyBuilder = new MultipartBodyBuilder(); + List actual = resolveArgument(param, bodyBuilder); + + assertThat(actual).isEmpty(); + } + @Test public void monoPart() { MethodParameter param = this.testMethod.annot(requestPart()).arg(Mono.class, Part.class); @@ -188,6 +225,15 @@ public class RequestPartMethodArgumentResolverTests { assertThat(partToUtf8String(part)).isEqualTo("{\"name\":\"Jones\"}"); } + @Test // gh-23060 + public void monoPartNotRequired() { + MethodParameter param = this.testMethod.annot(requestPart().notRequired()).arg(Mono.class, Part.class); + MultipartBodyBuilder bodyBuilder = new MultipartBodyBuilder(); + Mono actual = resolveArgument(param, bodyBuilder); + + assertThat(actual.block()).isNull(); + } + @Test public void fluxPart() { MethodParameter param = this.testMethod.annot(requestPart()).arg(Flux.class, Part.class); @@ -201,6 +247,15 @@ public class RequestPartMethodArgumentResolverTests { assertThat(partToUtf8String(parts.get(1))).isEqualTo("{\"name\":\"James\"}"); } + @Test // gh-23060 + public void fluxPartNotRequired() { + MethodParameter param = this.testMethod.annot(requestPart().notRequired()).arg(Flux.class, Part.class); + MultipartBodyBuilder bodyBuilder = new MultipartBodyBuilder(); + Flux actual = resolveArgument(param, bodyBuilder); + + assertThat(actual.collectList().block()).isEmpty(); + } + @Test public void personRequired() { MethodParameter param = this.testMethod.annot(requestPart()).arg(Person.class); @@ -278,7 +333,13 @@ public class RequestPartMethodArgumentResolverTests { @RequestPart("name") Flux partFlux, @RequestPart("name") List partList, @RequestPart(name = "anotherPart", required = false) Person anotherPerson, + @RequestPart(name = "name", required = false) Mono anotherPersonMono, + @RequestPart(name = "name", required = false) Flux anotherPersonFlux, + @RequestPart(name = "name", required = false) List anotherPersonList, @RequestPart(name = "anotherPart", required = false) Part anotherPart, + @RequestPart(name = "name", required = false) Mono anotherPartMono, + @RequestPart(name = "name", required = false) Flux anotherPartFlux, + @RequestPart(name = "name", required = false) List anotherPartList, Person notAnnotated) {}