Browse Source

Exception handler methods may receive original as well as cause

Issue: SPR-15701
pull/1468/merge
Juergen Hoeller 7 years ago
parent
commit
a6d616d844
  1. 15
      spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java
  2. 30
      spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestMappingExceptionHandlingIntegrationTests.java

15
spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java

@ -198,20 +198,25 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, Application
.onErrorResume(exceptionHandler))); .onErrorResume(exceptionHandler)));
} }
private Mono<HandlerResult> handleException(Throwable ex, HandlerMethod handlerMethod, private Mono<HandlerResult> handleException(Throwable exception, HandlerMethod handlerMethod,
BindingContext bindingContext, ServerWebExchange exchange) { BindingContext bindingContext, ServerWebExchange exchange) {
Assert.state(this.methodResolver != null, "Not initialized"); Assert.state(this.methodResolver != null, "Not initialized");
InvocableHandlerMethod invocable = this.methodResolver.getExceptionHandlerMethod(ex, handlerMethod); InvocableHandlerMethod invocable = this.methodResolver.getExceptionHandlerMethod(exception, handlerMethod);
if (invocable != null) { if (invocable != null) {
try { try {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Invoking @ExceptionHandler method: " + invocable.getMethod()); logger.debug("Invoking @ExceptionHandler method: " + invocable.getMethod());
} }
bindingContext.getModel().asMap().clear(); bindingContext.getModel().asMap().clear();
Throwable cause = ex.getCause() != null ? ex.getCause() : ex; Throwable cause = exception.getCause();
return invocable.invoke(exchange, bindingContext, cause, handlerMethod); if (cause != null) {
return invocable.invoke(exchange, bindingContext, exception, cause, handlerMethod);
}
else {
return invocable.invoke(exchange, bindingContext, exception, handlerMethod);
}
} }
catch (Throwable invocationEx) { catch (Throwable invocationEx) {
if (logger.isWarnEnabled()) { if (logger.isWarnEnabled()) {
@ -219,7 +224,7 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, Application
} }
} }
} }
return Mono.error(ex); return Mono.error(exception);
} }
} }

30
spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestMappingExceptionHandlingIntegrationTests.java

@ -16,6 +16,8 @@
package org.springframework.web.reactive.result.method.annotation; package org.springframework.web.reactive.result.method.annotation;
import java.io.IOException;
import org.junit.Test; import org.junit.Test;
import org.reactivestreams.Publisher; import org.reactivestreams.Publisher;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
@ -37,6 +39,7 @@ import static org.junit.Assert.*;
* {@code @RequestMapping} integration tests with exception handling scenarios. * {@code @RequestMapping} integration tests with exception handling scenarios.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @author Juergen Hoeller
*/ */
public class RequestMappingExceptionHandlingIntegrationTests extends AbstractRequestMappingIntegrationTests { public class RequestMappingExceptionHandlingIntegrationTests extends AbstractRequestMappingIntegrationTests {
@ -55,6 +58,18 @@ public class RequestMappingExceptionHandlingIntegrationTests extends AbstractReq
assertEquals(expected, performGet("/thrown-exception", new HttpHeaders(), String.class).getBody()); assertEquals(expected, performGet("/thrown-exception", new HttpHeaders(), String.class).getBody());
} }
@Test
public void controllerThrowingExceptionWithCause() throws Exception {
String expected = "Recovered from error: State";
assertEquals(expected, performGet("/thrown-exception-with-cause", new HttpHeaders(), String.class).getBody());
}
@Test
public void controllerThrowingExceptionWithCauseToHandle() throws Exception {
String expected = "Recovered from error: IO";
assertEquals(expected, performGet("/thrown-exception-with-cause-to-handle", new HttpHeaders(), String.class).getBody());
}
@Test @Test
public void controllerReturnsMonoError() throws Exception { public void controllerReturnsMonoError() throws Exception {
String expected = "Recovered from error: Argument"; String expected = "Recovered from error: Argument";
@ -79,11 +94,26 @@ public class RequestMappingExceptionHandlingIntegrationTests extends AbstractReq
throw new IllegalStateException("State"); throw new IllegalStateException("State");
} }
@GetMapping("/thrown-exception-with-cause")
public Publisher<String> handleAndThrowExceptionWithCause() {
throw new IllegalStateException("State", new IOException("IO"));
}
@GetMapping("/thrown-exception-with-cause-to-handle")
public Publisher<String> handleAndThrowExceptionWithCauseToHandle() {
throw new RuntimeException("State", new IOException("IO"));
}
@GetMapping("/mono-error") @GetMapping("/mono-error")
public Publisher<String> handleWithError() { public Publisher<String> handleWithError() {
return Mono.error(new IllegalArgumentException("Argument")); return Mono.error(new IllegalArgumentException("Argument"));
} }
@ExceptionHandler
public Publisher<String> handleArgumentException(IOException ex) {
return Mono.just("Recovered from error: " + ex.getMessage());
}
@ExceptionHandler @ExceptionHandler
public Publisher<String> handleArgumentException(IllegalArgumentException ex) { public Publisher<String> handleArgumentException(IllegalArgumentException ex) {
return Mono.just("Recovered from error: " + ex.getMessage()); return Mono.just("Recovered from error: " + ex.getMessage());

Loading…
Cancel
Save