Exposes all root causes to ExceptionHandler methods
Closes gh-28155
This commit is contained in:
parent
b6b03f38d7
commit
b30f4d7bb7
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2020 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package org.springframework.web.reactive.result.method.annotation;
|
package org.springframework.web.reactive.result.method.annotation;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
@ -213,21 +214,30 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, Application
|
||||||
|
|
||||||
InvocableHandlerMethod invocable = this.methodResolver.getExceptionHandlerMethod(exception, handlerMethod);
|
InvocableHandlerMethod invocable = this.methodResolver.getExceptionHandlerMethod(exception, handlerMethod);
|
||||||
if (invocable != null) {
|
if (invocable != null) {
|
||||||
|
ArrayList<Throwable> exceptions = new ArrayList<>();
|
||||||
try {
|
try {
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug(exchange.getLogPrefix() + "Using @ExceptionHandler " + invocable);
|
logger.debug(exchange.getLogPrefix() + "Using @ExceptionHandler " + invocable);
|
||||||
}
|
}
|
||||||
bindingContext.getModel().asMap().clear();
|
bindingContext.getModel().asMap().clear();
|
||||||
Throwable cause = exception.getCause();
|
|
||||||
if (cause != null) {
|
// Expose causes as provided arguments as well
|
||||||
return invocable.invoke(exchange, bindingContext, exception, cause, handlerMethod);
|
Throwable exToExpose = exception;
|
||||||
}
|
while (exToExpose != null) {
|
||||||
else {
|
exceptions.add(exToExpose);
|
||||||
return invocable.invoke(exchange, bindingContext, exception, handlerMethod);
|
Throwable cause = exToExpose.getCause();
|
||||||
|
exToExpose = (cause != exToExpose ? cause : null);
|
||||||
}
|
}
|
||||||
|
Object[] arguments = new Object[exceptions.size() + 1];
|
||||||
|
exceptions.toArray(arguments); // efficient arraycopy call in ArrayList
|
||||||
|
arguments[arguments.length - 1] = handlerMethod;
|
||||||
|
|
||||||
|
return invocable.invoke(exchange, bindingContext, arguments);
|
||||||
}
|
}
|
||||||
catch (Throwable invocationEx) {
|
catch (Throwable invocationEx) {
|
||||||
if (logger.isWarnEnabled()) {
|
// Any other than the original exception (or a cause) is unintended here,
|
||||||
|
// probably an accident (e.g. failed assertion or the like).
|
||||||
|
if (!exceptions.contains(invocationEx) && logger.isWarnEnabled()) {
|
||||||
logger.warn(exchange.getLogPrefix() + "Failure in @ExceptionHandler " + invocable, invocationEx);
|
logger.warn(exchange.getLogPrefix() + "Failure in @ExceptionHandler " + invocable, invocationEx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2019 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -82,9 +82,10 @@ public class ControllerAdviceTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void resolveExceptionWithAssertionErrorAsRootCause() throws Exception {
|
public void resolveExceptionWithAssertionErrorAsRootCause() throws Exception {
|
||||||
AssertionError cause = new AssertionError("argh");
|
AssertionError rootCause = new AssertionError("argh");
|
||||||
FatalBeanException exception = new FatalBeanException("wrapped", cause);
|
FatalBeanException cause = new FatalBeanException("wrapped", rootCause);
|
||||||
testException(exception, cause.toString());
|
Exception exception = new Exception(cause);
|
||||||
|
testException(exception, rootCause.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testException(Throwable exception, String expected) throws Exception {
|
private void testException(Throwable exception, String expected) throws Exception {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2019 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -138,7 +138,7 @@ class RequestMappingExceptionHandlingIntegrationTests extends AbstractRequestMap
|
||||||
|
|
||||||
@GetMapping("/thrown-exception-with-cause-to-handle")
|
@GetMapping("/thrown-exception-with-cause-to-handle")
|
||||||
public Publisher<String> handleAndThrowExceptionWithCauseToHandle() {
|
public Publisher<String> handleAndThrowExceptionWithCauseToHandle() {
|
||||||
throw new RuntimeException("State", new IOException("IO"));
|
throw new RuntimeException("State1", new RuntimeException("State2", new IOException("IO")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(path = "/mono-error")
|
@GetMapping(path = "/mono-error")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue