Allow treating empty @RequestParam as missing value

If type conversion turns an empty request parameter value (i.e. "") to
null, we should treat it as a missing value. By default the
ConversionService doesn't change empty strings and therefore one must
explicitly convert them to null for example by registering a
StringTrimmerEditor with emptyAsNull=true.

Issue: SPR-10402
This commit is contained in:
Rossen Stoyanchev 2013-05-10 12:31:00 -04:00
parent 399f887128
commit d3eda09c01
2 changed files with 32 additions and 7 deletions

View File

@ -74,6 +74,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
this.expressionContext = (beanFactory != null) ? new BeanExpressionContext(beanFactory, new RequestScope()) : null; this.expressionContext = (beanFactory != null) ? new BeanExpressionContext(beanFactory, new RequestScope()) : null;
} }
@Override
public final Object resolveArgument( public final Object resolveArgument(
MethodParameter parameter, ModelAndViewContainer mavContainer, MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
@ -96,11 +97,17 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
arg = resolveDefaultValue(namedValueInfo.defaultValue); arg = resolveDefaultValue(namedValueInfo.defaultValue);
} }
boolean emptyArgValue = "".equals(arg);
if (binderFactory != null) { if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name); WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
arg = binder.convertIfNecessary(arg, paramType, parameter); arg = binder.convertIfNecessary(arg, paramType, parameter);
} }
if (emptyArgValue && (arg == null)) {
handleMissingValue(namedValueInfo.name, parameter);
}
handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest); handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
return arg; return arg;

View File

@ -16,13 +16,6 @@
package org.springframework.web.method.annotation; package org.springframework.web.method.annotation;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@ -32,6 +25,7 @@ import javax.servlet.http.Part;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.propertyeditors.StringTrimmerEditor;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer; import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterNameDiscoverer; import org.springframework.core.ParameterNameDiscoverer;
@ -41,13 +35,20 @@ import org.springframework.mock.web.test.MockMultipartFile;
import org.springframework.mock.web.test.MockMultipartHttpServletRequest; import org.springframework.mock.web.test.MockMultipartHttpServletRequest;
import org.springframework.mock.web.test.MockPart; import org.springframework.mock.web.test.MockPart;
import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.bind.support.WebRequestDataBinder;
import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.multipart.MultipartException; import org.springframework.web.multipart.MultipartException;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import static org.junit.Assert.*;
import static org.mockito.BDDMockito.*;
import static org.mockito.Mockito.*;
/** /**
* Test fixture with {@link org.springframework.web.method.annotation.RequestParamMethodArgumentResolver}. * Test fixture with {@link org.springframework.web.method.annotation.RequestParamMethodArgumentResolver}.
* *
@ -242,6 +243,23 @@ public class RequestParamMethodArgumentResolverTests {
fail("Expected exception"); fail("Expected exception");
} }
// SPR-10402
@Test(expected = MissingServletRequestParameterException.class)
public void missingRequestParamEmptyValueConvertedToNull() throws Exception {
WebDataBinder binder = new WebRequestDataBinder(null);
binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
WebDataBinderFactory binderFactory = mock(WebDataBinderFactory.class);
given(binderFactory.createBinder(webRequest, null, "stringNotAnnot")).willReturn(binder);
this.request.addParameter("stringNotAnnot", "");
resolver.resolveArgument(paramStringNotAnnot, null, webRequest, binderFactory);
fail("Expected exception");
}
@Test @Test
public void resolveSimpleTypeParam() throws Exception { public void resolveSimpleTypeParam() throws Exception {
request.setParameter("stringNotAnnot", "plainValue"); request.setParameter("stringNotAnnot", "plainValue");