Fix handling of required payload.
A payload that is required will now throw an appropriate exception regardless of if a conversion is required or not. isEmptyPayload now takes the payload instead of the message so that both the original payload and the converted payload, if necessary, share the same logic. JSR-303 validation is now consistently applied. Issue: SPR-11577
This commit is contained in:
committed by
Rossen Stoyanchev
parent
4cd818b9e4
commit
52c3f713bf
@@ -23,31 +23,39 @@ import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.ObjectError;
|
||||
|
||||
/**
|
||||
* Exception to be thrown when validation on an method parameter annotated with {@code @Valid} fails.
|
||||
* Exception to be thrown when a method argument is not valid. For instance, this
|
||||
* can be issued if a validation on a method parameter annotated with
|
||||
* {@code @Valid} fails.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 4.0.1
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class MethodArgumentNotValidException extends MessagingException {
|
||||
|
||||
private final MethodParameter parameter;
|
||||
|
||||
private final BindingResult bindingResult;
|
||||
|
||||
|
||||
public MethodArgumentNotValidException(Message<?> message, MethodParameter parameter, BindingResult bindingResult) {
|
||||
super(message);
|
||||
this.parameter = parameter;
|
||||
this.bindingResult = bindingResult;
|
||||
/**
|
||||
* Create a new message with the given description.
|
||||
* @see #getMessage()
|
||||
*/
|
||||
public MethodArgumentNotValidException(Message<?> message, String description) {
|
||||
super(message, description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
/**
|
||||
* Create a new instance with a failed validation described by
|
||||
* the given {@link BindingResult}.
|
||||
*/
|
||||
public MethodArgumentNotValidException(Message<?> message,
|
||||
MethodParameter parameter, BindingResult bindingResult) {
|
||||
this(message, generateMessage(parameter, bindingResult));
|
||||
}
|
||||
|
||||
private static String generateMessage(MethodParameter parameter, BindingResult bindingResult) {
|
||||
StringBuilder sb = new StringBuilder("Validation failed for parameter at index ")
|
||||
.append(this.parameter.getParameterIndex()).append(" in method: ")
|
||||
.append(this.parameter.getMethod().toGenericString())
|
||||
.append(", with ").append(this.bindingResult.getErrorCount()).append(" error(s): ");
|
||||
for (ObjectError error : this.bindingResult.getAllErrors()) {
|
||||
.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("] ");
|
||||
}
|
||||
return sb.toString();
|
||||
|
||||
@@ -42,6 +42,7 @@ import java.lang.annotation.Annotation;
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Brian Clozel
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.0
|
||||
*/
|
||||
public class PayloadArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
@@ -65,36 +66,53 @@ public class PayloadArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
@Override
|
||||
public Object resolveArgument(MethodParameter parameter, Message<?> message) throws Exception {
|
||||
|
||||
Class<?> sourceClass = message.getPayload().getClass();
|
||||
Class<?> targetClass = parameter.getParameterType();
|
||||
|
||||
if (ClassUtils.isAssignable(targetClass,sourceClass)) {
|
||||
return message.getPayload();
|
||||
}
|
||||
|
||||
Payload annot = parameter.getParameterAnnotation(Payload.class);
|
||||
|
||||
if (isEmptyPayload(message)) {
|
||||
if ((annot != null) && !annot.required()) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if ((annot != null) && StringUtils.hasText(annot.value())) {
|
||||
throw new IllegalStateException("@Payload SpEL expressions not supported by this resolver.");
|
||||
}
|
||||
|
||||
Object target = this.converter.fromMessage(message, targetClass);
|
||||
validate(message, parameter, target);
|
||||
Object target = getTargetPayload(parameter, message);
|
||||
if (annot != null && isEmptyPayload(target)) {
|
||||
if (annot.required()) {
|
||||
throw new MethodArgumentNotValidException(message, createPayloadRequiredExceptionMessage(parameter, target));
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (annot != null) { // Only validate @Payload
|
||||
validate(message, parameter, target);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
protected boolean isEmptyPayload(Message<?> message) {
|
||||
Object payload = message.getPayload();
|
||||
if (payload instanceof byte[]) {
|
||||
return ((byte[]) message.getPayload()).length == 0;
|
||||
/**
|
||||
* 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}.
|
||||
*/
|
||||
protected Object getTargetPayload(MethodParameter parameter, Message<?> message) {
|
||||
Class<?> sourceClass = message.getPayload().getClass();
|
||||
Class<?> targetClass = parameter.getParameterType();
|
||||
if (ClassUtils.isAssignable(targetClass,sourceClass)) {
|
||||
return message.getPayload();
|
||||
}
|
||||
return this.converter.fromMessage(message, targetClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify if the given {@code payload} is empty.
|
||||
* @param payload the payload to check (can be {@code null})
|
||||
*/
|
||||
protected boolean isEmptyPayload(Object payload) {
|
||||
if (payload == null) {
|
||||
return true;
|
||||
}
|
||||
else if (payload instanceof byte[]) {
|
||||
return ((byte[]) payload).length == 0;
|
||||
}
|
||||
else if (payload instanceof String) {
|
||||
return ((String) payload).trim().equals("");
|
||||
@@ -128,4 +146,19 @@ 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user