From dbc69284c8ff8a1bcd11d0116299b56710cf301f Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Mon, 29 Jul 2024 09:12:44 +0300 Subject: [PATCH] ResponseEntityExceptionHandler handles AsyncRequestNotUsableException Closes gh-33225 --- .../ResponseEntityExceptionHandler.java | 27 ++++++++++++++++--- .../ResponseEntityExceptionHandlerTests.java | 12 ++++++--- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java index d0446adf84..900aae9651 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java @@ -48,6 +48,7 @@ import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.context.request.WebRequest; +import org.springframework.web.context.request.async.AsyncRequestNotUsableException; import org.springframework.web.context.request.async.AsyncRequestTimeoutException; import org.springframework.web.method.annotation.HandlerMethodValidationException; import org.springframework.web.multipart.MaxUploadSizeExceededException; @@ -135,7 +136,8 @@ public abstract class ResponseEntityExceptionHandler implements MessageSourceAwa HttpMessageNotReadableException.class, HttpMessageNotWritableException.class, MethodValidationException.class, - BindException.class + BindException.class, + AsyncRequestNotUsableException.class }) @Nullable public final ResponseEntity handleException(Exception ex, WebRequest request) throws Exception { @@ -197,8 +199,11 @@ public abstract class ResponseEntityExceptionHandler implements MessageSourceAwa else if (ex instanceof HttpMessageNotWritableException theEx) { return handleHttpMessageNotWritable(theEx, headers, HttpStatus.INTERNAL_SERVER_ERROR, request); } - else if (ex instanceof MethodValidationException subEx) { - return handleMethodValidationException(subEx, headers, HttpStatus.INTERNAL_SERVER_ERROR, request); + else if (ex instanceof MethodValidationException theEx) { + return handleMethodValidationException(theEx, headers, HttpStatus.INTERNAL_SERVER_ERROR, request); + } + else if (ex instanceof AsyncRequestNotUsableException theEx) { + return handleAsyncRequestNotUsableException(theEx, request); } else { // Unknown exception, typically a wrapper with a common MVC exception as cause @@ -568,6 +573,22 @@ public abstract class ResponseEntityExceptionHandler implements MessageSourceAwa return handleExceptionInternal(ex, body, headers, status, request); } + /** + * Customize the handling of {@link AsyncRequestNotUsableException}. + *

By default, return {@code null} since the response is not usable. + * @param ex the exception to handle + * @param request the current request + * @return a {@code ResponseEntity} for the response to use, possibly + * {@code null} when the response is already committed + * @since 6.2 + */ + @Nullable + protected ResponseEntity handleAsyncRequestNotUsableException( + AsyncRequestNotUsableException ex, WebRequest request) { + + return null; + } + /** * Convenience method to create a {@link ProblemDetail} for any exception * that doesn't implement {@link ErrorResponse}, also performing a diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandlerTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandlerTests.java index 4435963152..600b91f239 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandlerTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandlerTests.java @@ -57,6 +57,7 @@ import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.context.request.WebRequest; +import org.springframework.web.context.request.async.AsyncRequestNotUsableException; import org.springframework.web.context.request.async.AsyncRequestTimeoutException; import org.springframework.web.context.support.StaticWebApplicationContext; import org.springframework.web.method.annotation.HandlerMethodValidationException; @@ -107,10 +108,6 @@ class ResponseEntityExceptionHandlerTests { .filter(method -> method.getName().startsWith("handle") && (method.getParameterCount() == 4)) .filter(method -> !method.getName().equals("handleErrorResponse")) .map(method -> method.getParameterTypes()[0]) - .filter(exceptionType -> { - String name = exceptionType.getSimpleName(); - return !name.equals("AsyncRequestNotUsableException"); - }) .forEach(exceptionType -> assertThat(annotation.value()) .as("@ExceptionHandler is missing declaration for " + exceptionType.getName()) .contains((Class) exceptionType)); @@ -316,6 +313,13 @@ class ResponseEntityExceptionHandlerTests { testException(new AsyncRequestTimeoutException()); } + @Test + void asyncRequestNotUsableException() throws Exception { + AsyncRequestNotUsableException ex = new AsyncRequestNotUsableException("simulated failure"); + ResponseEntity entity = this.exceptionHandler.handleException(ex, this.request); + assertThat(entity).isNull(); + } + @Test void maxUploadSizeExceededException() { testException(new MaxUploadSizeExceededException(1000));