diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/ServletModelAttributeMethodProcessor.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/ServletModelAttributeMethodProcessor.java index b435d5f2ca..66e3aa3adf 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/ServletModelAttributeMethodProcessor.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/ServletModelAttributeMethodProcessor.java @@ -22,6 +22,10 @@ import java.util.Map; import javax.servlet.ServletRequest; import org.springframework.core.MethodParameter; +import org.springframework.core.convert.ConversionService; +import org.springframework.core.convert.TypeDescriptor; +import org.springframework.core.convert.converter.Converter; +import org.springframework.util.StringUtils; import org.springframework.validation.DataBinder; import org.springframework.web.bind.ServletRequestDataBinder; import org.springframework.web.bind.WebDataBinder; @@ -36,9 +40,9 @@ import org.springframework.web.servlet.mvc.method.annotation.ServletRequestDataB * A Servlet-specific {@link ModelAttributeMethodProcessor} that applies data * binding through a WebDataBinder of type {@link ServletRequestDataBinder}. * - *
Adds a fall-back strategy to instantiate a model attribute from a - * URI template variable combined with type conversion, if the model attribute - * name matches to a URI template variable name. + *
Also adds a fall-back strategy to instantiate the model attribute from a
+ * URI template variable or from a request parameter if the name matches the
+ * model attribute name and there is an appropriate type conversion strategy.
*
* @author Rossen Stoyanchev
* @since 3.1
@@ -56,36 +60,89 @@ public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodPr
}
/**
- * Add a fall-back strategy to instantiate the model attribute from a URI
- * template variable with type conversion, if the model attribute name
- * matches to a URI variable name.
+ * Instantiate the model attribute from a URI template variable or from a
+ * request parameter if the name matches to the model attribute name and
+ * if there is an appropriate type conversion strategy. If none of these
+ * are true delegate back to the base class.
+ * @see #createAttributeFromUriValue
*/
@Override
- protected Object createAttribute(String attributeName,
- MethodParameter parameter,
- WebDataBinderFactory binderFactory,
- NativeWebRequest request) throws Exception {
-
- Map The default implementation looks for the attribute name to match
+ * a URI variable first and then a request parameter.
+ * @param attributeName the model attribute name
+ * @param request the current request
+ * @return the request value to try to convert or {@code null}
+ */
+ protected String getRequestValueForAttribute(String attributeName, NativeWebRequest request) {
+ Map The default implementation converts only if there a registered
+ * {@link Converter} that can perform the conversion.
+ * @param sourceValue the source value to create the model attribute from
+ * @param attributeName the name of the attribute, never {@code null}
+ * @param parameter the method parameter
+ * @param binderFactory for creating WebDataBinder instance
+ * @param request the current request
+ * @return the created model attribute, or {@code null}
+ * @throws Exception
+ */
+ protected Object createAttributeFromRequestValue(String sourceValue,
+ String attributeName,
+ MethodParameter parameter,
+ WebDataBinderFactory binderFactory,
+ NativeWebRequest request) throws Exception {
+ DataBinder binder = binderFactory.createBinder(request, null, attributeName);
+ ConversionService conversionService = binder.getConversionService();
+ if (conversionService != null) {
+ TypeDescriptor source = TypeDescriptor.valueOf(String.class);
+ TypeDescriptor target = new TypeDescriptor(parameter);
+ if (conversionService.canConvert(source, target)) {
+ return binder.convertIfNecessary(sourceValue, parameter.getParameterType(), parameter);
+ }
+ }
+ return null;
+ }
+
/**
* {@inheritDoc}
* Downcast {@link WebDataBinder} to {@link ServletRequestDataBinder} before binding.
diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/SerlvetModelAttributeMethodProcessorTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/SerlvetModelAttributeMethodProcessorTests.java
index abc85ab3d9..64732552f5 100644
--- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/SerlvetModelAttributeMethodProcessorTests.java
+++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/SerlvetModelAttributeMethodProcessorTests.java
@@ -27,8 +27,10 @@ import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.TestBean;
import org.springframework.core.MethodParameter;
+import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.ServletWebRequest;
@@ -60,44 +62,72 @@ public class SerlvetModelAttributeMethodProcessorTests {
@Before
public void setUp() throws Exception {
- processor = new ServletModelAttributeMethodProcessor(false);
+ this.processor = new ServletModelAttributeMethodProcessor(false);
Method method = getClass().getDeclaredMethod("modelAttribute",
TestBean.class, TestBeanWithoutStringConstructor.class);
- testBeanModelAttr = new MethodParameter(method, 0);
- testBeanWithoutStringConstructorModelAttr = new MethodParameter(method, 1);
+ this.testBeanModelAttr = new MethodParameter(method, 0);
+ this.testBeanWithoutStringConstructorModelAttr = new MethodParameter(method, 1);
- binderFactory = new ServletRequestDataBinderFactory(null, null);
- mavContainer = new ModelAndViewContainer();
+ ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
+ initializer.setConversionService(new DefaultConversionService());
- request = new MockHttpServletRequest();
- webRequest = new ServletWebRequest(request);
+ this.binderFactory = new ServletRequestDataBinderFactory(null, initializer );
+ this.mavContainer = new ModelAndViewContainer();
+
+ this.request = new MockHttpServletRequest();
+ this.webRequest = new ServletWebRequest(request);
}
@Test
- public void createAttributeViaPathVariable() throws Exception {
+ public void createAttributeUriTemplateVar() throws Exception {
Map