#122 - Fixed parameter binding for optional request parameters.

If the method arguments handed into a dummy controller method invocation are required but null we now throw an exception as we cannot build a URI that hits the very same method in the very same way. Based on that, we now inspect @RequestParam annotated parameters for null values, allow them but don't bind a parameter for them anymore.
This commit is contained in:
Oliver Gierke
2014-01-02 12:15:47 +01:00
parent 9b172631f9
commit 653f28727c
3 changed files with 91 additions and 3 deletions

View File

@@ -65,12 +65,41 @@ class AnnotatedParametersParameterAccessor {
List<BoundMethodParameter> result = new ArrayList<BoundMethodParameter>();
for (MethodParameter parameter : parameters.getParametersWith(attribute.getAnnotationType())) {
result.add(new BoundMethodParameter(parameter, arguments[parameter.getParameterIndex()], attribute));
Object value = arguments[parameter.getParameterIndex()];
Object verifiedValue = verifyParameterValue(parameter, value);
if (verifiedValue != null) {
result.add(new BoundMethodParameter(parameter, value, attribute));
}
}
return result;
}
/**
* Callback to verifiy the parameter values given for a dummy invocation. Default implementation rejects
* {@literal null} values as they indicate an invalid dummy call.
*
* @param parameter will never be {@literal null}.
* @param value could be {@literal null}.
* @return the verified value.
*/
protected Object verifyParameterValue(MethodParameter parameter, Object value) {
if (value == null) {
Object indexOrName = StringUtils.hasText(parameter.getParameterName()) ? parameter.getParameterName() : parameter
.getParameterIndex();
throw new IllegalArgumentException(String.format(
"Required controller parameter %s of method %s found but null value given!", indexOrName,
parameter.getMethod()));
}
return value;
}
/**
* Represents a {@link MethodParameter} alongside the value it has been bound to.
*

View File

@@ -56,8 +56,7 @@ public class ControllerLinkBuilderFactory implements MethodLinkBuilderFactory<Co
private static final MappingDiscoverer DISCOVERER = new AnnotationMappingDiscoverer(RequestMapping.class);
private static final AnnotatedParametersParameterAccessor PATH_VARIABLE_ACCESSOR = new AnnotatedParametersParameterAccessor(
new AnnotationAttribute(PathVariable.class));
private static final AnnotatedParametersParameterAccessor REQUEST_PARAM_ACCESSOR = new AnnotatedParametersParameterAccessor(
new AnnotationAttribute(RequestParam.class));
private static final AnnotatedParametersParameterAccessor REQUEST_PARAM_ACCESSOR = new RequestParamParameterAccessor();
private List<UriComponentsContributor> uriComponentsContributors = new ArrayList<UriComponentsContributor>();
@@ -169,4 +168,28 @@ public class ControllerLinkBuilderFactory implements MethodLinkBuilderFactory<Co
return builder;
}
/**
* Custom extension of {@link AnnotatedParametersParameterAccessor} for {@link RequestParam} to allow {@literal null}
* values handed in for optional request parameters.
*
* @author Oliver Gierke
*/
private static class RequestParamParameterAccessor extends AnnotatedParametersParameterAccessor {
public RequestParamParameterAccessor() {
super(new AnnotationAttribute(RequestParam.class));
}
/*
* (non-Javadoc)
* @see org.springframework.hateoas.mvc.AnnotatedParametersParameterAccessor#verifyParameterValue(org.springframework.core.MethodParameter, java.lang.Object)
*/
@Override
protected Object verifyParameterValue(MethodParameter parameter, Object value) {
RequestParam annotation = parameter.getParameterAnnotation(RequestParam.class);
return annotation.required() ? super.verifyParameterValue(parameter, value) : value;
}
}
}