Skip to content

Commit

Permalink
Exclude Part and MultipartFile from nested constructor binding
Browse files Browse the repository at this point in the history
Closes gh-31669
  • Loading branch information
rstoyanchev committed Nov 30, 2023
1 parent feef98b commit 9ade52d
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -948,7 +948,7 @@ private Object createObject(ResolvableType objectType, String nestedPath, ValueR
Class<?> paramType = paramTypes[i];
Object value = valueResolver.resolveValue(paramPath, paramType);

if (value == null && shouldCreateObject(param)) {
if (value == null && shouldConstructArgument(param)) {
ResolvableType type = ResolvableType.forMethodParameter(param);
args[i] = createObject(type, paramPath + ".", valueResolver);
}
Expand Down Expand Up @@ -1008,7 +1008,14 @@ private Object createObject(ResolvableType objectType, String nestedPath, ValueR
return (isOptional && !nestedPath.isEmpty() ? Optional.ofNullable(result) : result);
}

private static boolean shouldCreateObject(MethodParameter param) {
/**
* Whether to instantiate the constructor argument of the given type,
* matching its own constructor arguments to bind values.
* <p>By default, simple value types, maps, collections, and arrays are
* excluded from nested constructor binding initialization.
* @since 6.1.2
*/
protected boolean shouldConstructArgument(MethodParameter param) {
Class<?> type = param.nestedIfOptional().getNestedParameterType();
return !(BeanUtils.isSimpleValueType(type) ||
Collection.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type) || type.isArray());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import jakarta.servlet.http.Part;

import org.springframework.beans.MutablePropertyValues;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.lang.Nullable;
Expand Down Expand Up @@ -120,6 +121,13 @@ protected ServletRequestValueResolver createValueResolver(ServletRequest request
return new ServletRequestValueResolver(request, this);
}

@Override
protected boolean shouldConstructArgument(MethodParameter param) {
Class<?> type = param.nestedIfOptional().getNestedParameterType();
return (super.shouldConstructArgument(param) &&
!MultipartFile.class.isAssignableFrom(type) && !Part.class.isAssignableFrom(type));
}

/**
* Bind the parameters of the given request to this binder's target,
* also binding multipart files in case of a multipart request.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@

import jakarta.servlet.ServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.Part;

import org.springframework.beans.MutablePropertyValues;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
Expand All @@ -30,6 +32,7 @@
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartRequest;
import org.springframework.web.multipart.support.StandardServletPartUtils;

Expand Down Expand Up @@ -118,6 +121,12 @@ public void construct(WebRequest request) {
}
}

@Override
protected boolean shouldConstructArgument(MethodParameter param) {
Class<?> type = param.nestedIfOptional().getNestedParameterType();
return (super.shouldConstructArgument(param) &&
!MultipartFile.class.isAssignableFrom(type) && !Part.class.isAssignableFrom(type));
}

/**
* Bind the parameters of the given request to this binder's target,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.SynthesizingMethodParameter;
import org.springframework.format.support.DefaultFormattingConversionService;
import org.springframework.lang.Nullable;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Errors;
import org.springframework.web.bind.MethodArgumentNotValidException;
Expand All @@ -41,6 +42,7 @@
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;

import static java.lang.annotation.ElementType.CONSTRUCTOR;
Expand Down Expand Up @@ -298,6 +300,7 @@ public void resolveConstructorListArgumentFromCommaSeparatedRequestParameter() t
Object resolved = this.processor.resolveArgument(this.beanWithConstructorArgs, this.container, requestWithParam, factory);
assertThat(resolved).isInstanceOf(TestBeanWithConstructorArgs.class);
assertThat(((TestBeanWithConstructorArgs) resolved).listOfStrings).containsExactly("1", "2");
assertThat(((TestBeanWithConstructorArgs) resolved).file).isNull();
}

private void testGetAttributeFromModel(String expectedAttrName, MethodParameter param) throws Exception {
Expand Down Expand Up @@ -375,8 +378,11 @@ static class TestBeanWithConstructorArgs {

final List<String> listOfStrings;

public TestBeanWithConstructorArgs(List<String> listOfStrings) {
final MultipartFile file;

public TestBeanWithConstructorArgs(List<String> listOfStrings, @Nullable MultipartFile file) {
this.listOfStrings = listOfStrings;
this.file = file;
}
}

Expand Down

0 comments on commit 9ade52d

Please sign in to comment.