From a6d616d8444fb29626df15a1e4943203ad030fe1 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 5 Jul 2017 13:39:30 +0200 Subject: [PATCH] Exception handler methods may receive original as well as cause Issue: SPR-15701 --- .../RequestMappingHandlerAdapter.java | 15 ++++++---- ...pingExceptionHandlingIntegrationTests.java | 30 +++++++++++++++++++ 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java index 20659708ca..472204c763 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java +++ b/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))); } - private Mono handleException(Throwable ex, HandlerMethod handlerMethod, + private Mono handleException(Throwable exception, HandlerMethod handlerMethod, BindingContext bindingContext, ServerWebExchange exchange) { Assert.state(this.methodResolver != null, "Not initialized"); - InvocableHandlerMethod invocable = this.methodResolver.getExceptionHandlerMethod(ex, handlerMethod); + InvocableHandlerMethod invocable = this.methodResolver.getExceptionHandlerMethod(exception, handlerMethod); if (invocable != null) { try { if (logger.isDebugEnabled()) { logger.debug("Invoking @ExceptionHandler method: " + invocable.getMethod()); } bindingContext.getModel().asMap().clear(); - Throwable cause = ex.getCause() != null ? ex.getCause() : ex; - return invocable.invoke(exchange, bindingContext, cause, handlerMethod); + Throwable cause = exception.getCause(); + if (cause != null) { + return invocable.invoke(exchange, bindingContext, exception, cause, handlerMethod); + } + else { + return invocable.invoke(exchange, bindingContext, exception, handlerMethod); + } } catch (Throwable invocationEx) { if (logger.isWarnEnabled()) { @@ -219,7 +224,7 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, Application } } } - return Mono.error(ex); + return Mono.error(exception); } } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestMappingExceptionHandlingIntegrationTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestMappingExceptionHandlingIntegrationTests.java index 2ac7301ccc..94c78002ba 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestMappingExceptionHandlingIntegrationTests.java +++ b/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; +import java.io.IOException; + import org.junit.Test; import org.reactivestreams.Publisher; import reactor.core.publisher.Mono; @@ -37,6 +39,7 @@ import static org.junit.Assert.*; * {@code @RequestMapping} integration tests with exception handling scenarios. * * @author Rossen Stoyanchev + * @author Juergen Hoeller */ public class RequestMappingExceptionHandlingIntegrationTests extends AbstractRequestMappingIntegrationTests { @@ -55,6 +58,18 @@ public class RequestMappingExceptionHandlingIntegrationTests extends AbstractReq 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 public void controllerReturnsMonoError() throws Exception { String expected = "Recovered from error: Argument"; @@ -79,11 +94,26 @@ public class RequestMappingExceptionHandlingIntegrationTests extends AbstractReq throw new IllegalStateException("State"); } + @GetMapping("/thrown-exception-with-cause") + public Publisher handleAndThrowExceptionWithCause() { + throw new IllegalStateException("State", new IOException("IO")); + } + + @GetMapping("/thrown-exception-with-cause-to-handle") + public Publisher handleAndThrowExceptionWithCauseToHandle() { + throw new RuntimeException("State", new IOException("IO")); + } + @GetMapping("/mono-error") public Publisher handleWithError() { return Mono.error(new IllegalArgumentException("Argument")); } + @ExceptionHandler + public Publisher handleArgumentException(IOException ex) { + return Mono.just("Recovered from error: " + ex.getMessage()); + } + @ExceptionHandler public Publisher handleArgumentException(IllegalArgumentException ex) { return Mono.just("Recovered from error: " + ex.getMessage());