Skip to content

Commit dbc6928

Browse files
committed
ResponseEntityExceptionHandler handles AsyncRequestNotUsableException
Closes gh-33225
1 parent 5ac7e74 commit dbc6928

File tree

2 files changed

+32
-7
lines changed

2 files changed

+32
-7
lines changed

spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java

+24-3
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import org.springframework.web.bind.annotation.ExceptionHandler;
4949
import org.springframework.web.context.request.ServletWebRequest;
5050
import org.springframework.web.context.request.WebRequest;
51+
import org.springframework.web.context.request.async.AsyncRequestNotUsableException;
5152
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
5253
import org.springframework.web.method.annotation.HandlerMethodValidationException;
5354
import org.springframework.web.multipart.MaxUploadSizeExceededException;
@@ -135,7 +136,8 @@ protected MessageSource getMessageSource() {
135136
HttpMessageNotReadableException.class,
136137
HttpMessageNotWritableException.class,
137138
MethodValidationException.class,
138-
BindException.class
139+
BindException.class,
140+
AsyncRequestNotUsableException.class
139141
})
140142
@Nullable
141143
public final ResponseEntity<Object> handleException(Exception ex, WebRequest request) throws Exception {
@@ -197,8 +199,11 @@ else if (ex instanceof HttpMessageNotReadableException theEx) {
197199
else if (ex instanceof HttpMessageNotWritableException theEx) {
198200
return handleHttpMessageNotWritable(theEx, headers, HttpStatus.INTERNAL_SERVER_ERROR, request);
199201
}
200-
else if (ex instanceof MethodValidationException subEx) {
201-
return handleMethodValidationException(subEx, headers, HttpStatus.INTERNAL_SERVER_ERROR, request);
202+
else if (ex instanceof MethodValidationException theEx) {
203+
return handleMethodValidationException(theEx, headers, HttpStatus.INTERNAL_SERVER_ERROR, request);
204+
}
205+
else if (ex instanceof AsyncRequestNotUsableException theEx) {
206+
return handleAsyncRequestNotUsableException(theEx, request);
202207
}
203208
else {
204209
// Unknown exception, typically a wrapper with a common MVC exception as cause
@@ -568,6 +573,22 @@ protected ResponseEntity<Object> handleMethodValidationException(
568573
return handleExceptionInternal(ex, body, headers, status, request);
569574
}
570575

576+
/**
577+
* Customize the handling of {@link AsyncRequestNotUsableException}.
578+
* <p>By default, return {@code null} since the response is not usable.
579+
* @param ex the exception to handle
580+
* @param request the current request
581+
* @return a {@code ResponseEntity} for the response to use, possibly
582+
* {@code null} when the response is already committed
583+
* @since 6.2
584+
*/
585+
@Nullable
586+
protected ResponseEntity<Object> handleAsyncRequestNotUsableException(
587+
AsyncRequestNotUsableException ex, WebRequest request) {
588+
589+
return null;
590+
}
591+
571592
/**
572593
* Convenience method to create a {@link ProblemDetail} for any exception
573594
* that doesn't implement {@link ErrorResponse}, also performing a

spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandlerTests.java

+8-4
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
import org.springframework.web.bind.annotation.RequestMapping;
5858
import org.springframework.web.context.request.ServletWebRequest;
5959
import org.springframework.web.context.request.WebRequest;
60+
import org.springframework.web.context.request.async.AsyncRequestNotUsableException;
6061
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
6162
import org.springframework.web.context.support.StaticWebApplicationContext;
6263
import org.springframework.web.method.annotation.HandlerMethodValidationException;
@@ -107,10 +108,6 @@ void supportsAllDefaultHandlerExceptionResolverExceptionTypes() throws Exception
107108
.filter(method -> method.getName().startsWith("handle") && (method.getParameterCount() == 4))
108109
.filter(method -> !method.getName().equals("handleErrorResponse"))
109110
.map(method -> method.getParameterTypes()[0])
110-
.filter(exceptionType -> {
111-
String name = exceptionType.getSimpleName();
112-
return !name.equals("AsyncRequestNotUsableException");
113-
})
114111
.forEach(exceptionType -> assertThat(annotation.value())
115112
.as("@ExceptionHandler is missing declaration for " + exceptionType.getName())
116113
.contains((Class<Exception>) exceptionType));
@@ -316,6 +313,13 @@ void asyncRequestTimeoutException() {
316313
testException(new AsyncRequestTimeoutException());
317314
}
318315

316+
@Test
317+
void asyncRequestNotUsableException() throws Exception {
318+
AsyncRequestNotUsableException ex = new AsyncRequestNotUsableException("simulated failure");
319+
ResponseEntity<Object> entity = this.exceptionHandler.handleException(ex, this.request);
320+
assertThat(entity).isNull();
321+
}
322+
319323
@Test
320324
void maxUploadSizeExceededException() {
321325
testException(new MaxUploadSizeExceededException(1000));

0 commit comments

Comments
 (0)