Raise MessageConversionException in @Payload resolver

If a payload is present but conversion returns null (meaning no
converter knows how to convert), raise a MessageConversionException
that provides information about the type we were trying to convert
to and the message itself whose headers (namely content-type) contain
crucial information required to debug the problem.

Issue: SPR-11577
This commit is contained in:
Rossen Stoyanchev 2014-03-21 08:52:31 -04:00
parent 26309838ba
commit 929e9ca401
2 changed files with 40 additions and 33 deletions

View File

@ -19,6 +19,7 @@ package org.springframework.messaging.handler.annotation.support;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.messaging.Message;
import org.springframework.messaging.converter.MessageConversionException;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;
import org.springframework.messaging.converter.MessageConverter;
@ -74,12 +75,12 @@ public class PayloadArgumentResolver implements HandlerMethodArgumentResolver {
throw new IllegalStateException("@Payload SpEL expressions not supported by this resolver.");
}
Object target = getTargetPayload(param, message);
if (isEmptyPayload(target)) {
Object payload = message.getPayload();
if (isEmptyPayload(payload)) {
if (annot == null || annot.required()) {
String paramName = param.getParameterName();
paramName = (paramName == null ? "Arg" + param.getParameterIndex() : paramName);
BindingResult bindingResult = new BeanPropertyBindingResult(target, paramName);
String paramName = getParameterName(param);
BindingResult bindingResult = new BeanPropertyBindingResult(payload, paramName);
bindingResult.addError(new ObjectError(paramName, "@Payload param is required"));
throw new MethodArgumentNotValidException(message, param, bindingResult);
}
@ -88,27 +89,25 @@ public class PayloadArgumentResolver implements HandlerMethodArgumentResolver {
}
}
if (annot != null) { // Only validate @Payload
validate(message, param, target);
Class<?> targetClass = param.getParameterType();
if (ClassUtils.isAssignable(targetClass, payload.getClass())) {
validate(message, param, payload);
return payload;
}
else {
payload = this.converter.fromMessage(message, targetClass);
if (payload == null) {
throw new MessageConversionException(message,
"No converter found to convert to " + targetClass + ", message=" + message, null);
}
validate(message, param, payload);
return payload;
}
return target;
}
/**
* 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();
Class<?> targetClass = parameter.getParameterType();
if (ClassUtils.isAssignable(targetClass,sourceClass)) {
return message.getPayload();
}
return this.converter.fromMessage(message, targetClass);
private String getParameterName(MethodParameter param) {
String paramName = param.getParameterName();
return (paramName == null ? "Arg " + param.getParameterIndex() : paramName);
}
/**
@ -131,17 +130,24 @@ public class PayloadArgumentResolver implements HandlerMethodArgumentResolver {
}
protected void validate(Message<?> message, MethodParameter parameter, Object target) {
Annotation[] annotations = parameter.getParameterAnnotations();
for (Annotation annot : annotations) {
if (this.validator == null) {
return;
}
for (Annotation annot : parameter.getParameterAnnotations()) {
if (annot.annotationType().getSimpleName().startsWith("Valid")) {
BeanPropertyBindingResult bindingResult = new BeanPropertyBindingResult(target, parameter.getParameterName());
BeanPropertyBindingResult bindingResult =
new BeanPropertyBindingResult(target, getParameterName(parameter));
Object hints = AnnotationUtils.getValue(annot);
Object[] validationHints = hints instanceof Object[] ? (Object[]) hints : new Object[] {hints};
Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
if (!ObjectUtils.isEmpty(validationHints) && this.validator instanceof SmartValidator) {
((SmartValidator) this.validator).validate(target, bindingResult, validationHints);
}
else if (this.validator != null) {
else {
this.validator.validate(target, bindingResult);
}

View File

@ -29,6 +29,7 @@ import org.junit.rules.ExpectedException;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.MethodParameter;
import org.springframework.messaging.Message;
import org.springframework.messaging.converter.MessageConversionException;
import org.springframework.messaging.converter.StringMessageConverter;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.support.MessageBuilder;
@ -129,9 +130,8 @@ public class PayloadArgumentResolverTests {
public void resolveNonConvertibleParam() throws Exception {
Message<?> notEmptyMessage = MessageBuilder.withPayload(123).build();
// Could not convert from int to Locale so will be "empty" after conversion
thrown.expect(MethodArgumentNotValidException.class);
thrown.expectMessage(Locale.class.getName()); // reference to the type that could not be converted
thrown.expect(MessageConversionException.class);
thrown.expectMessage("No converter found");
this.resolver.resolveArgument(this.paramAnnotatedRequired, notEmptyMessage);
}
@ -183,8 +183,9 @@ public class PayloadArgumentResolverTests {
// See testValidator()
Message<?> message = MessageBuilder.withPayload("invalidValue".getBytes()).build();
assertEquals("invalidValue",
this.resolver.resolveArgument(paramValidatedNotAnnotated, message));
thrown.expect(MethodArgumentNotValidException.class);
thrown.expectMessage("invalid value");
assertEquals("invalidValue", this.resolver.resolveArgument(this.paramValidatedNotAnnotated, message));
}
private Validator testValidator() {