Support for populating model attributes through data class constructors

Includes a new overloaded ModelAndView constructor with an HttpStatus argument, as well as a HandlerMethodArgumentResolverSupport refactoring (revised checkParameterType signature, actually implementing the HandlerMethodArgumentResolver interface).

Issue: SPR-15199
This commit is contained in:
Juergen Hoeller
2017-03-24 12:15:45 +01:00
parent b3154357f0
commit 65ba865d70
30 changed files with 411 additions and 274 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
package org.springframework.web.servlet.mvc.method.annotation;
import java.beans.ConstructorProperties;
import java.beans.PropertyEditorSupport;
import java.io.IOException;
import java.io.Serializable;
@@ -140,14 +141,7 @@ import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.support.RequestContextUtils;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assert.*;
/**
* @author Rossen Stoyanchev
@@ -283,15 +277,13 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
}, NestedSetController.class);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do");
request.addParameter("testBeanSet", new String[] {"1", "2"});
request.addParameter("testBeanSet", "1", "2");
MockHttpServletResponse response = new MockHttpServletResponse();
getServlet().service(request, response);
assertEquals("[1, 2]-org.springframework.tests.sample.beans.TestBean", response.getContentAsString());
}
// SPR-12903
@Test
@Test // SPR-12903
public void pathVariableWithCustomConverter() throws Exception {
initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
@Override
@@ -1244,9 +1236,7 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
assertEquals("myParam-42", response.getContentAsString());
}
// SPR-9062
@Test
@Test // SPR-9062
public void ambiguousPathAndRequestMethod() throws Exception {
initServletWithControllers(AmbiguousPathAndRequestMethodController.class);
@@ -1498,9 +1488,7 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
assertEquals(405, response.getStatus());
}
// SPR-8536
@Test
@Test // SPR-8536
public void testHeadersCondition() throws Exception {
initServletWithControllers(HeadersConditionController.class);
@@ -1571,7 +1559,7 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
assertTrue(RequestContextUtils.getOutputFlashMap(request).isEmpty());
}
@Test // SPR-15176
@Test // SPR-15176
public void flashAttributesWithResponseEntity() throws Exception {
initServletWithControllers(RedirectAttributesController.class);
@@ -1831,6 +1819,31 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
assertTrue(response.getContentAsByteArray().length == 0);
}
@Test
public void dataClassBinding() throws ServletException, IOException {
initServletWithControllers(DataClassController.class);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/bind");
request.addParameter("param1", "value1");
request.addParameter("param2", "2");
MockHttpServletResponse response = new MockHttpServletResponse();
getServlet().service(request, response);
assertEquals("value1-2-0", response.getContentAsString());
}
@Test
public void dataClassBindingWithAdditionalSetter() throws ServletException, IOException {
initServletWithControllers(DataClassController.class);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/bind");
request.addParameter("param1", "value1");
request.addParameter("param2", "2");
request.addParameter("param3", "3");
MockHttpServletResponse response = new MockHttpServletResponse();
getServlet().service(request, response);
assertEquals("value1-2-3", response.getContentAsString());
}
@Controller
static class ControllerWithEmptyValueMapping {
@@ -2727,15 +2740,12 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
@XmlRootElement
public static class A {
}
@XmlRootElement
public static class B {
}
public static class NotReadableMessageConverter implements HttpMessageConverter<Object> {
@Override
@@ -3313,7 +3323,6 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
public HttpHeaders createNoHeader() throws URISyntaxException {
return new HttpHeaders();
}
}
@RestController
@@ -3345,7 +3354,7 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
@RequestMapping("/path")
public ModelAndView methodWithHttpStatus(MyEntity object) {
return new ModelAndView("view", new ModelMap(), HttpStatus.UNPROCESSABLE_ENTITY);
return new ModelAndView("view", HttpStatus.UNPROCESSABLE_ENTITY);
}
@RequestMapping("/exception")
@@ -3355,7 +3364,7 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
@ExceptionHandler(TestException.class)
public ModelAndView handleException() {
return new ModelAndView("view", new ModelMap(), HttpStatus.UNPROCESSABLE_ENTITY);
return new ModelAndView("view", HttpStatus.UNPROCESSABLE_ENTITY);
}
@SuppressWarnings("serial")
@@ -3363,4 +3372,32 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
}
}
public static class DataClass {
public final String param1;
public final int param2;
public int param3;
@ConstructorProperties({"param1", "param2"})
public DataClass(String param1, int p2) {
this.param1 = param1;
this.param2 = p2;
}
public void setParam3(int param3) {
this.param3 = param3;
}
}
@RestController
public static class DataClassController {
@RequestMapping("/bind")
public String handle(DataClass data) {
return data.param1 + "-" + data.param2 + "-" + data.param3;
}
}
}