Consistent support for path variable and multipart binding

Closes gh-24107
Closes gh-22169
Closes gh-25265
This commit is contained in:
Juergen Hoeller
2020-08-06 18:28:09 +02:00
parent 5bdbbdfcfb
commit d61c0ee57d
13 changed files with 365 additions and 85 deletions

View File

@@ -57,6 +57,7 @@ import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.http.Part;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import javax.xml.bind.annotation.XmlRootElement;
@@ -108,6 +109,7 @@ import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.util.Assert;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Errors;
@@ -139,6 +141,7 @@ import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.context.support.GenericWebApplicationContext;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.support.StringMultipartFileEditor;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.ModelAndView;
@@ -158,6 +161,7 @@ import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import org.springframework.web.testfixture.servlet.MockHttpServletResponse;
import org.springframework.web.testfixture.servlet.MockMultipartFile;
import org.springframework.web.testfixture.servlet.MockMultipartHttpServletRequest;
import org.springframework.web.testfixture.servlet.MockPart;
import org.springframework.web.testfixture.servlet.MockServletConfig;
import org.springframework.web.testfixture.servlet.MockServletContext;
@@ -1934,6 +1938,44 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
assertThat(response.getContentAsString()).isEqualTo("value1-true-0");
}
@PathPatternsParameterizedTest
void dataClassBindingWithPathVariable(boolean usePathPatterns) throws Exception {
initDispatcherServlet(PathVariableDataClassController.class, usePathPatterns);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/bind/true");
request.addParameter("param1", "value1");
MockHttpServletResponse response = new MockHttpServletResponse();
getServlet().service(request, response);
assertThat(response.getContentAsString()).isEqualTo("value1-true-0");
}
@PathPatternsParameterizedTest
void dataClassBindingWithMultipartFile(boolean usePathPatterns) throws Exception {
initDispatcherServlet(MultipartFileDataClassController.class, usePathPatterns);
MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest();
request.setRequestURI("/bind");
request.addFile(new MockMultipartFile("param1", "value1".getBytes(StandardCharsets.UTF_8)));
request.addParameter("param2", "true");
MockHttpServletResponse response = new MockHttpServletResponse();
getServlet().service(request, response);
assertThat(response.getContentAsString()).isEqualTo("value1-true-0");
}
@PathPatternsParameterizedTest
void dataClassBindingWithServletPart(boolean usePathPatterns) throws Exception {
initDispatcherServlet(ServletPartDataClassController.class, usePathPatterns);
MockHttpServletRequest request = new MockHttpServletRequest();
request.setContentType("multipart/form-data");
request.setRequestURI("/bind");
request.addPart(new MockPart("param1", "value1".getBytes(StandardCharsets.UTF_8)));
request.addParameter("param2", "true");
MockHttpServletResponse response = new MockHttpServletResponse();
getServlet().service(request, response);
assertThat(response.getContentAsString()).isEqualTo("value1-true-0");
}
@PathPatternsParameterizedTest
void dataClassBindingWithAdditionalSetter(boolean usePathPatterns) throws Exception {
initDispatcherServlet(DataClassController.class, usePathPatterns);
@@ -3831,6 +3873,15 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
}
}
@RestController
public static class PathVariableDataClassController {
@RequestMapping("/bind/{param2}")
public String handle(DataClass data) {
return data.param1 + "-" + data.param2 + "-" + data.param3;
}
}
@RestController
public static class ValidatedDataClassController {
@@ -3873,6 +3924,70 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
}
}
public static class MultipartFileDataClass {
@NotNull
public final MultipartFile param1;
public final boolean param2;
public int param3;
@ConstructorProperties({"param1", "param2", "optionalParam"})
public MultipartFileDataClass(MultipartFile param1, boolean p2, Optional<Integer> optionalParam) {
this.param1 = param1;
this.param2 = p2;
Assert.notNull(optionalParam, "Optional must not be null");
optionalParam.ifPresent(integer -> this.param3 = integer);
}
public void setParam3(int param3) {
this.param3 = param3;
}
}
@RestController
public static class MultipartFileDataClassController {
@RequestMapping("/bind")
public String handle(MultipartFileDataClass data) throws IOException {
return StreamUtils.copyToString(data.param1.getInputStream(), StandardCharsets.UTF_8) +
"-" + data.param2 + "-" + data.param3;
}
}
public static class ServletPartDataClass {
@NotNull
public final Part param1;
public final boolean param2;
public int param3;
@ConstructorProperties({"param1", "param2", "optionalParam"})
public ServletPartDataClass(Part param1, boolean p2, Optional<Integer> optionalParam) {
this.param1 = param1;
this.param2 = p2;
Assert.notNull(optionalParam, "Optional must not be null");
optionalParam.ifPresent(integer -> this.param3 = integer);
}
public void setParam3(int param3) {
this.param3 = param3;
}
}
@RestController
public static class ServletPartDataClassController {
@RequestMapping("/bind")
public String handle(ServletPartDataClass data) throws IOException {
return StreamUtils.copyToString(data.param1.getInputStream(), StandardCharsets.UTF_8) +
"-" + data.param2 + "-" + data.param3;
}
}
@RestController
public static class OptionalDataClassController {