SPR-6909 Include URI template vars in data binding
This commit is contained in:
parent
5c27a04210
commit
50117dce40
|
|
@ -42,6 +42,7 @@ import org.springframework.http.converter.StringHttpMessageConverter;
|
|||
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
|
||||
import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter;
|
||||
import org.springframework.util.ReflectionUtils.MethodFilter;
|
||||
import org.springframework.validation.DataBinder;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.InitBinder;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
|
|
@ -257,13 +258,19 @@ public class RequestMappingHandlerMethodAdapter extends AbstractHandlerMethodAda
|
|||
}
|
||||
|
||||
/**
|
||||
* Specify a WebBindingInitializer which will apply pre-configured
|
||||
* configuration to every DataBinder that this controller uses.
|
||||
* Set a WebBindingInitializer to apply configure every DataBinder instance this controller uses.
|
||||
*/
|
||||
public void setWebBindingInitializer(WebBindingInitializer webBindingInitializer) {
|
||||
this.webBindingInitializer = webBindingInitializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the WebBindingInitializer which applies pre-configured configuration to {@link DataBinder} instances.
|
||||
*/
|
||||
public WebBindingInitializer getWebBindingInitializer() {
|
||||
return webBindingInitializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the strategy to store session attributes with.
|
||||
* <p>Default is {@link org.springframework.web.bind.support.DefaultSessionAttributeStore},
|
||||
|
|
|
|||
|
|
@ -17,12 +17,17 @@
|
|||
package org.springframework.web.servlet.mvc.method.annotation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.web.bind.ServletRequestDataBinder;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.support.WebBindingInitializer;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.method.annotation.InitBinderMethodDataBinderFactory;
|
||||
import org.springframework.web.method.support.InvocableHandlerMethod;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
|
||||
/**
|
||||
* An {@link InitBinderMethodDataBinderFactory} that creates a {@link ServletRequestDataBinder}.
|
||||
|
|
@ -47,7 +52,30 @@ public class ServletInitBinderMethodDataBinderFactory extends InitBinderMethodDa
|
|||
*/
|
||||
@Override
|
||||
protected WebDataBinder createBinderInstance(Object target, String objectName) {
|
||||
return new ServletRequestDataBinder(target, objectName);
|
||||
return new ServletRequestPathVarDataBinder(target, objectName);
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* Adds URI template variables to the map of request values used to do data binding.
|
||||
*/
|
||||
private static class ServletRequestPathVarDataBinder extends ServletRequestDataBinder {
|
||||
|
||||
public ServletRequestPathVarDataBinder(Object target, String objectName) {
|
||||
super(target, objectName);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected void doBind(MutablePropertyValues mpvs) {
|
||||
RequestAttributes requestAttrs = RequestContextHolder.getRequestAttributes();
|
||||
if (requestAttrs != null) {
|
||||
String key = HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE;
|
||||
int scope = RequestAttributes.SCOPE_REQUEST;
|
||||
Map<String, String> uriTemplateVars = (Map<String, String>) requestAttrs.getAttribute(key, scope);
|
||||
mpvs.addPropertyValues(uriTemplateVars);
|
||||
}
|
||||
super.doBind(mpvs);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.servlet.mvc.method.annotation;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.TestBean;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.web.bind.ServletRequestDataBinder;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
|
||||
/**
|
||||
* Test fixture with {@link ServletInitBinderMethodDataBinderFactory}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class ServletInitBinderMethodDataBinderFactoryTests {
|
||||
|
||||
private ServletInitBinderMethodDataBinderFactory binderFactory;
|
||||
|
||||
private MockHttpServletRequest request;
|
||||
|
||||
private NativeWebRequest webRequest;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
binderFactory = new ServletInitBinderMethodDataBinderFactory(null, null);
|
||||
request = new MockHttpServletRequest();
|
||||
webRequest = new ServletWebRequest(request);
|
||||
RequestContextHolder.setRequestAttributes(webRequest);
|
||||
}
|
||||
|
||||
@After
|
||||
public void teardown() {
|
||||
RequestContextHolder.resetRequestAttributes();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createBinder() throws Exception {
|
||||
Map<String, String> uriTemplateVars = new HashMap<String, String>();
|
||||
uriTemplateVars.put("name", "nameValue");
|
||||
uriTemplateVars.put("age", "25");
|
||||
request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVars);
|
||||
|
||||
TestBean target = new TestBean();
|
||||
WebDataBinder binder = binderFactory.createBinder(webRequest, target, "");
|
||||
((ServletRequestDataBinder) binder).bind(request);
|
||||
|
||||
assertEquals("nameValue", target.getName());
|
||||
assertEquals(25, target.getAge());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package org.springframework.web.method.annotation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
|
@ -41,13 +42,13 @@ public class InitBinderMethodDataBinderFactory extends DefaultDataBinderFactory
|
|||
|
||||
/**
|
||||
* Create an {@code InitBinderMethodDataBinderFactory} instance with the given {@link InitBinder} methods.
|
||||
* @param initBinderMethods {@link InitBinder} methods to use to invoke to initialize new data binder instances
|
||||
* @param binderMethods {@link InitBinder} methods to use to invoke to initialize new data binder instances
|
||||
* @param bindingInitializer a {@link WebBindingInitializer} to initialize new data binder instances with
|
||||
*/
|
||||
public InitBinderMethodDataBinderFactory(List<InvocableHandlerMethod> initBinderMethods,
|
||||
public InitBinderMethodDataBinderFactory(List<InvocableHandlerMethod> binderMethods,
|
||||
WebBindingInitializer bindingInitializer) {
|
||||
super(bindingInitializer);
|
||||
this.initBinderMethods = initBinderMethods;
|
||||
this.initBinderMethods = (binderMethods != null) ? binderMethods : new ArrayList<InvocableHandlerMethod>();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -94,11 +94,11 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
|
|||
if (binder.getTarget() != null) {
|
||||
doBind(binder, webRequest);
|
||||
|
||||
if (shouldValidate(parameter)) {
|
||||
if (shouldValidate(binder, parameter)) {
|
||||
binder.validate();
|
||||
}
|
||||
|
||||
if (failOnError(parameter) && binder.getBindingResult().hasErrors()) {
|
||||
if (failOnError(binder, parameter) && binder.getBindingResult().hasErrors()) {
|
||||
throw new BindException(binder.getBindingResult());
|
||||
}
|
||||
}
|
||||
|
|
@ -133,9 +133,12 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
|
|||
}
|
||||
|
||||
/**
|
||||
* Whether to validate the target object of the given {@link WebDataBinder} instance.
|
||||
* @param binder the data binder containing the validation candidate
|
||||
* @param parameter the method argument for which data binding is performed
|
||||
* @return true if {@link DataBinder#validate()} should be invoked, false otherwise.
|
||||
*/
|
||||
protected boolean shouldValidate(MethodParameter parameter) {
|
||||
protected boolean shouldValidate(WebDataBinder binder, MethodParameter parameter) {
|
||||
Annotation[] annotations = parameter.getParameterAnnotations();
|
||||
for (Annotation annot : annotations) {
|
||||
if ("Valid".equals(annot.annotationType().getSimpleName())) {
|
||||
|
|
@ -146,9 +149,12 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
|
|||
}
|
||||
|
||||
/**
|
||||
* Whether to raise a {@link BindException} in case of data binding or validation errors.
|
||||
* @param binder the binder on which validation is to be invoked
|
||||
* @param parameter the method argument for which data binding is performed
|
||||
* @return true if the binding or validation errors should result in a {@link BindException}, false otherwise.
|
||||
*/
|
||||
protected boolean failOnError(MethodParameter parameter) {
|
||||
protected boolean failOnError(WebDataBinder binder, MethodParameter parameter) {
|
||||
int i = parameter.getParameterIndex();
|
||||
Class<?>[] paramTypes = parameter.getMethod().getParameterTypes();
|
||||
boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1]));
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@ import org.springframework.util.StringUtils;
|
|||
import org.springframework.validation.support.BindingAwareModelMap;
|
||||
|
||||
/**
|
||||
* Provides access to the model and a place to record model and view related decisions to all
|
||||
* {@link HandlerMethodArgumentResolver}s and {@link HandlerMethodReturnValueHandler}s .
|
||||
* Provides access to the model and a place to record model and view related decisions made by
|
||||
* {@link HandlerMethodArgumentResolver}s or a {@link HandlerMethodReturnValueHandler}.
|
||||
*
|
||||
* <p>In addition to storing model attributes and a view, the {@link ModelAndViewContainer} also provides
|
||||
* a {@link #setResolveView(boolean)} flag, which can be used to request or bypass a view resolution phase.
|
||||
|
|
|
|||
|
|
@ -132,14 +132,14 @@ public class ModelAttributeMethodProcessorTests {
|
|||
|
||||
@Test
|
||||
public void shouldValidate() throws Exception {
|
||||
assertTrue(processor.shouldValidate(paramNamedValidModelAttr));
|
||||
assertFalse(processor.shouldValidate(paramNonSimpleType));
|
||||
assertTrue(processor.shouldValidate(null, paramNamedValidModelAttr));
|
||||
assertFalse(processor.shouldValidate(null, paramNonSimpleType));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failOnError() throws Exception {
|
||||
assertFalse("Shouldn't failOnError with BindingResult", processor.failOnError(paramNamedValidModelAttr));
|
||||
assertTrue("Should failOnError without BindingResult", processor.failOnError(paramNonSimpleType));
|
||||
assertFalse("Shouldn't failOnError with BindingResult", processor.failOnError(null, paramNamedValidModelAttr));
|
||||
assertTrue("Should failOnError without BindingResult", processor.failOnError(null, paramNonSimpleType));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
Loading…
Reference in New Issue