Ensure default value of @Payload required is enforced

When no @Payload is provided, it is equivalent to @Payload with default
attribute values. Since the default value of required=true, then
an argument that's not annotated is required.
This commit is contained in:
Rossen Stoyanchev
2014-03-20 19:44:19 -04:00
parent 52c3f713bf
commit d4782647a4
3 changed files with 117 additions and 74 deletions

View File

@@ -33,31 +33,61 @@ import org.springframework.validation.ObjectError;
@SuppressWarnings("serial")
public class MethodArgumentNotValidException extends MessagingException {
private final MethodParameter parameter;
private final BindingResult bindingResult;
/**
* Create a new message with the given description.
* @see #getMessage()
* Create a new instance with the invalid {@code MethodParameter}.
*/
public MethodArgumentNotValidException(Message<?> message, String description) {
super(message, description);
public MethodArgumentNotValidException(Message<?> message, MethodParameter parameter) {
this(message, parameter, null);
}
/**
* Create a new instance with a failed validation described by
* the given {@link BindingResult}.
* Create a new instance with the invalid {@code MethodParameter} and a
* {@link org.springframework.validation.BindingResult}.
*/
public MethodArgumentNotValidException(Message<?> message,
MethodParameter parameter, BindingResult bindingResult) {
this(message, generateMessage(parameter, bindingResult));
public MethodArgumentNotValidException(Message<?> message, MethodParameter parameter,
BindingResult bindingResult) {
super(message, generateMessage(parameter, bindingResult));
this.parameter = parameter;
this.bindingResult = bindingResult;
}
/**
* Return the MethodParameter that was rejected.
*/
public MethodParameter getMethodParameter() {
return this.parameter;
}
/**
* Return the BindingResult if the failure is validation-related or {@code null}.
*/
public BindingResult getBindingResult() {
return this.bindingResult;
}
private static String generateMessage(MethodParameter parameter, BindingResult bindingResult) {
StringBuilder sb = new StringBuilder("Validation failed for parameter at index ")
StringBuilder sb = new StringBuilder("Invalid parameter at index ")
.append(parameter.getParameterIndex()).append(" in method: ")
.append(parameter.getMethod().toGenericString())
.append(", with ").append(bindingResult.getErrorCount()).append(" error(s): ");
for (ObjectError error : bindingResult.getAllErrors()) {
sb.append("[").append(error).append("] ");
.append(parameter.getMethod().toGenericString());
if (bindingResult != null) {
sb.append(", with ").append(bindingResult.getErrorCount()).append(" error(s): ");
for (ObjectError error : bindingResult.getAllErrors()) {
sb.append("[").append(error).append("] ");
}
}
return sb.toString();
}

View File

@@ -27,6 +27,8 @@ import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.validation.SmartValidator;
import org.springframework.validation.Validator;
@@ -65,16 +67,21 @@ public class PayloadArgumentResolver implements HandlerMethodArgumentResolver {
}
@Override
public Object resolveArgument(MethodParameter parameter, Message<?> message) throws Exception {
Payload annot = parameter.getParameterAnnotation(Payload.class);
public Object resolveArgument(MethodParameter param, Message<?> message) throws Exception {
Payload annot = param.getParameterAnnotation(Payload.class);
if ((annot != null) && StringUtils.hasText(annot.value())) {
throw new IllegalStateException("@Payload SpEL expressions not supported by this resolver.");
}
Object target = getTargetPayload(parameter, message);
if (annot != null && isEmptyPayload(target)) {
if (annot.required()) {
throw new MethodArgumentNotValidException(message, createPayloadRequiredExceptionMessage(parameter, target));
Object target = getTargetPayload(param, message);
if (isEmptyPayload(target)) {
if (annot == null || annot.required()) {
String paramName = param.getParameterName();
paramName = (paramName == null ? "Arg" + param.getParameterIndex() : paramName);
BindingResult bindingResult = new BeanPropertyBindingResult(target, paramName);
bindingResult.addError(new ObjectError(paramName, "@Payload param is required"));
throw new MethodArgumentNotValidException(message, param, bindingResult);
}
else {
return null;
@@ -82,17 +89,18 @@ public class PayloadArgumentResolver implements HandlerMethodArgumentResolver {
}
if (annot != null) { // Only validate @Payload
validate(message, parameter, target);
validate(message, param, target);
}
return target;
}
/**
* Return the target payload to handle for the specified message. Can either
* be the payload itself if the parameter type supports it or the converted
* one otherwise. While the payload of a {@link Message} cannot be null by
* design, this method may return a {@code null} payload if the conversion
* result is {@code null}.
* Return the payload for the specified message, which can be the payload
* itself if it matches the parameter type or the result of message conversion
* otherwise.
*
* <p>While the payload of a {@link Message} cannot be {@code null} by design,
* this method may return {@code null} if the message converter returns that.
*/
protected Object getTargetPayload(MethodParameter parameter, Message<?> message) {
Class<?> sourceClass = message.getPayload().getClass();
@@ -146,19 +154,4 @@ public class PayloadArgumentResolver implements HandlerMethodArgumentResolver {
}
}
private String createPayloadRequiredExceptionMessage(MethodParameter parameter, Object payload) {
String name = parameter.getParameterName() != null
? parameter.getParameterName() : "arg" + parameter.getParameterIndex();
StringBuilder sb = new StringBuilder("Payload parameter '").append(name)
.append(" at index ").append(parameter.getParameterIndex()).append(" ");
if (payload == null) {
sb.append("could not be converted to '").append(parameter.getParameterType().getName())
.append("' and is required");
}
else {
sb.append("is required");
}
return sb.toString();
}
}