Revised InvocableHandlerMethod exception messages (controller vs endpoint vs handler)
Introduces dedicated MethodArgumentResolutionException for spring-messaging invocations. Issue: SPR-15139
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -45,6 +45,7 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
* a {@link ServerWebExchange} and use that to invoke the underlying method.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Juergen Hoeller
|
||||
* @since 5.0
|
||||
*/
|
||||
public class InvocableHandlerMethod extends HandlerMethod {
|
||||
@@ -89,14 +90,13 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
||||
|
||||
/**
|
||||
* Invoke the method for the given exchange.
|
||||
*
|
||||
* @param exchange the current exchange
|
||||
* @param bindingContext the binding context to use
|
||||
* @param providedArgs optional list of argument values to match by type
|
||||
* @return Mono with a {@link HandlerResult}.
|
||||
*/
|
||||
public Mono<HandlerResult> invoke(ServerWebExchange exchange,
|
||||
BindingContext bindingContext, Object... providedArgs) {
|
||||
public Mono<HandlerResult> invoke(ServerWebExchange exchange, BindingContext bindingContext,
|
||||
Object... providedArgs) {
|
||||
|
||||
return resolveArguments(exchange, bindingContext, providedArgs).then(args -> {
|
||||
try {
|
||||
@@ -108,14 +108,13 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
||||
return Mono.error(ex.getTargetException());
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
String msg = getInvocationErrorMessage(args);
|
||||
return Mono.error(new IllegalStateException(msg));
|
||||
return Mono.error(new IllegalStateException(getInvocationErrorMessage(args)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Mono<Object[]> resolveArguments(ServerWebExchange exchange,
|
||||
BindingContext bindingContext, Object... providedArgs) {
|
||||
private Mono<Object[]> resolveArguments(ServerWebExchange exchange, BindingContext bindingContext,
|
||||
Object... providedArgs) {
|
||||
|
||||
if (ObjectUtils.isEmpty(getMethodParameters())) {
|
||||
return EMPTY_ARGS;
|
||||
@@ -125,7 +124,7 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
||||
.map(param -> {
|
||||
param.initParameterNameDiscovery(this.parameterNameDiscoverer);
|
||||
GenericTypeResolver.resolveParameterType(param, getBean().getClass());
|
||||
return findProvidedArg(param, providedArgs)
|
||||
return findProvidedArgument(param, providedArgs)
|
||||
.map(Mono::just)
|
||||
.orElseGet(() -> {
|
||||
HandlerMethodArgumentResolver resolver = findResolver(param);
|
||||
@@ -144,12 +143,12 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<Object> findProvidedArg(MethodParameter param, Object... providedArgs) {
|
||||
private Optional<Object> findProvidedArgument(MethodParameter parameter, Object... providedArgs) {
|
||||
if (ObjectUtils.isEmpty(providedArgs)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return Arrays.stream(providedArgs)
|
||||
.filter(arg -> param.getParameterType().isInstance(arg))
|
||||
.filter(arg -> parameter.getParameterType().isInstance(arg))
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
@@ -157,34 +156,33 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
||||
return this.resolvers.stream()
|
||||
.filter(r -> r.supportsParameter(param))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> getArgumentError("No resolver for ", param, null));
|
||||
.orElseThrow(() -> getArgumentError("No suitable resolver for", param, null));
|
||||
}
|
||||
|
||||
private Mono<Object> resolveArg(HandlerMethodArgumentResolver resolver, MethodParameter param,
|
||||
private Mono<Object> resolveArg(HandlerMethodArgumentResolver resolver, MethodParameter parameter,
|
||||
BindingContext bindingContext, ServerWebExchange exchange) {
|
||||
|
||||
try {
|
||||
return resolver.resolveArgument(param, bindingContext, exchange)
|
||||
return resolver.resolveArgument(parameter, bindingContext, exchange)
|
||||
.defaultIfEmpty(NO_ARG_VALUE)
|
||||
.doOnError(cause -> {
|
||||
if(logger.isDebugEnabled()) {
|
||||
logger.debug(getDetailedErrorMessage("Error resolving ", param), cause);
|
||||
logger.debug(getDetailedErrorMessage("Failed to resolve", parameter), cause);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw getArgumentError("Error resolving ", param, ex);
|
||||
throw getArgumentError("Failed to resolve", parameter, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private IllegalStateException getArgumentError(String message, MethodParameter param, Throwable ex) {
|
||||
return new IllegalStateException(getDetailedErrorMessage(message, param), ex);
|
||||
private IllegalStateException getArgumentError(String text, MethodParameter parameter, Throwable ex) {
|
||||
return new IllegalStateException(getDetailedErrorMessage(text, parameter), ex);
|
||||
}
|
||||
|
||||
private String getDetailedErrorMessage(String message, MethodParameter param) {
|
||||
return message + "argument [" + param.getParameterIndex() + "] " +
|
||||
"of type [" + param.getParameterType().getName() + "] " +
|
||||
"on method [" + getBridgedMethod().toGenericString() + "]";
|
||||
private String getDetailedErrorMessage(String text, MethodParameter param) {
|
||||
return text + " argument " + param.getParameterIndex() + " of type '" +
|
||||
param.getParameterType().getName() + "' on " + getBridgedMethod().toGenericString();
|
||||
}
|
||||
|
||||
private Object doInvoke(Object[] args) throws Exception {
|
||||
@@ -207,8 +205,8 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
||||
"[" + i + "][type=" + args[i].getClass().getName() + "][value=" + args[i] + "]" :
|
||||
"[" + i + "][null]"))
|
||||
.collect(Collectors.joining(",", " ", " "));
|
||||
return "Failed to invoke controller with resolved arguments:" + argumentDetails +
|
||||
"on method [" + getBridgedMethod().toGenericString() + "]";
|
||||
return "Failed to invoke handler method with resolved arguments:" + argumentDetails +
|
||||
"on " + getBridgedMethod().toGenericString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -34,18 +34,16 @@ import org.springframework.web.server.UnsupportedMediaTypeStatusException;
|
||||
import org.springframework.web.server.adapter.DefaultServerWebExchange;
|
||||
import org.springframework.web.server.session.MockWebSessionManager;
|
||||
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link InvocableHandlerMethod}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
@SuppressWarnings("ThrowableResultOfMethodCallIgnored")
|
||||
public class InvocableHandlerMethodTests {
|
||||
@@ -93,14 +91,14 @@ public class InvocableHandlerMethodTests {
|
||||
InvocableHandlerMethod hm = handlerMethod("singleArg");
|
||||
Mono<HandlerResult> mono = hm.invoke(this.exchange, new BindingContext());
|
||||
|
||||
StepVerifier.create(mono)
|
||||
.expectNextCount(0)
|
||||
.consumeErrorWith(error -> {
|
||||
assertThat(error, instanceOf(IllegalStateException.class));
|
||||
assertThat(error.getMessage(), is("No resolver for argument [0] of type [java.lang.String] " +
|
||||
"on method [" + hm.getMethod().toGenericString() + "]"));
|
||||
})
|
||||
.verify();
|
||||
try {
|
||||
mono.block();
|
||||
fail("Expected IllegalStateException");
|
||||
}
|
||||
catch (IllegalStateException ex) {
|
||||
assertThat(ex.getMessage(), is("No suitable resolver for argument 0 of type 'java.lang.String' " +
|
||||
"on " + hm.getMethod().toGenericString()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -109,13 +107,13 @@ public class InvocableHandlerMethodTests {
|
||||
addResolver(hm, Mono.error(new UnsupportedMediaTypeStatusException("boo")));
|
||||
Mono<HandlerResult> mono = hm.invoke(this.exchange, new BindingContext());
|
||||
|
||||
StepVerifier.create(mono)
|
||||
.expectNextCount(0)
|
||||
.consumeErrorWith(error -> {
|
||||
assertThat(error, instanceOf(UnsupportedMediaTypeStatusException.class));
|
||||
assertThat(error.getMessage(), is("Request failure [status: 415, reason: \"boo\"]"));
|
||||
})
|
||||
.verify();
|
||||
try {
|
||||
mono.block();
|
||||
fail("Expected UnsupportedMediaTypeStatusException");
|
||||
}
|
||||
catch (UnsupportedMediaTypeStatusException ex) {
|
||||
assertThat(ex.getMessage(), is("Request failure [status: 415, reason: \"boo\"]"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -124,15 +122,15 @@ public class InvocableHandlerMethodTests {
|
||||
addResolver(hm, Mono.just(1));
|
||||
Mono<HandlerResult> mono = hm.invoke(this.exchange, new BindingContext());
|
||||
|
||||
StepVerifier.create(mono)
|
||||
.expectNextCount(0)
|
||||
.consumeErrorWith(error -> {
|
||||
assertThat(error, instanceOf(IllegalStateException.class));
|
||||
assertThat(error.getMessage(), is("Failed to invoke controller with resolved arguments: " +
|
||||
"[0][type=java.lang.Integer][value=1] " +
|
||||
"on method [" + hm.getMethod().toGenericString() + "]"));
|
||||
})
|
||||
.verify();
|
||||
try {
|
||||
mono.block();
|
||||
fail("Expected IllegalStateException");
|
||||
}
|
||||
catch (IllegalStateException ex) {
|
||||
assertThat(ex.getMessage(), is("Failed to invoke handler method with resolved arguments: " +
|
||||
"[0][type=java.lang.Integer][value=1] " +
|
||||
"on " + hm.getMethod().toGenericString()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -140,13 +138,13 @@ public class InvocableHandlerMethodTests {
|
||||
InvocableHandlerMethod hm = handlerMethod("exceptionMethod");
|
||||
Mono<HandlerResult> mono = hm.invoke(this.exchange, new BindingContext());
|
||||
|
||||
StepVerifier.create(mono)
|
||||
.expectNextCount(0)
|
||||
.consumeErrorWith(error -> {
|
||||
assertThat(error, instanceOf(IllegalStateException.class));
|
||||
assertThat(error.getMessage(), is("boo"));
|
||||
})
|
||||
.verify();
|
||||
try {
|
||||
mono.block();
|
||||
fail("Expected IllegalStateException");
|
||||
}
|
||||
catch (IllegalStateException ex) {
|
||||
assertThat(ex.getMessage(), is("boo"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user