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 9aac2e3c72..2c4903f540 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
@@ -25,8 +25,8 @@ import org.springframework.beans.TypeMismatchException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
+import org.springframework.http.ProblemDetail;
import org.springframework.http.ResponseEntity;
-import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.lang.Nullable;
@@ -50,24 +50,16 @@ import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.util.WebUtils;
/**
- * A convenient base class for {@link ControllerAdvice @ControllerAdvice} classes
- * that wish to provide centralized exception handling across all
- * {@code @RequestMapping} methods through {@code @ExceptionHandler} methods.
+ * A class with an {@code @ExceptionHandler} method that handles all Spring MVC
+ * raised exceptions by returning a {@link ResponseEntity} with RFC-7807
+ * formatted error details in the body.
*
- *
This base class provides an {@code @ExceptionHandler} method for handling
- * internal Spring MVC exceptions. This method returns a {@code ResponseEntity}
- * for writing to the response with a {@link HttpMessageConverter message converter},
- * in contrast to
- * {@link org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
- * DefaultHandlerExceptionResolver} which returns a
- * {@link org.springframework.web.servlet.ModelAndView ModelAndView}.
- *
- *
If there is no need to write error content to the response body, or when
- * using view resolution (e.g., via {@code ContentNegotiatingViewResolver}),
- * then {@code DefaultHandlerExceptionResolver} is good enough.
- *
- *
Note that in order for an {@code @ControllerAdvice} subclass to be
- * detected, {@link ExceptionHandlerExceptionResolver} must be configured.
+ *
Convenient as a base class of an {@link ControllerAdvice @ControllerAdvice}
+ * for global exception handling in an application. Subclasses can override
+ * individual methods that handle a specific exception, override
+ * {@link #handleExceptionInternal} to override common handling of all exceptions,
+ * or {@link #createResponseEntity} to intercept the final step of creating the
+ * @link ResponseEntity} from the selected HTTP status code, headers, and body.
*
* @author Rossen Stoyanchev
* @since 3.2
@@ -95,8 +87,8 @@ public abstract class ResponseEntityExceptionHandler {
/**
- * Provides handling for standard Spring MVC exceptions.
- * @param ex the target exception
+ * Handle all exceptions raised within Spring MVC handling of the request .
+ * @param ex the exception to handle
* @param request the current request
*/
@ExceptionHandler({
@@ -121,61 +113,56 @@ public abstract class ResponseEntityExceptionHandler {
public final ResponseEntity handleException(Exception ex, WebRequest request) throws Exception {
HttpHeaders headers = new HttpHeaders();
- // ErrorResponse exceptions that expose HTTP response details
-
- if (ex instanceof ErrorResponse errorEx) {
- if (ex instanceof HttpRequestMethodNotSupportedException subEx) {
- return handleHttpRequestMethodNotSupported(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);
- }
- else if (ex instanceof HttpMediaTypeNotSupportedException subEx) {
- return handleHttpMediaTypeNotSupported(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);
- }
- else if (ex instanceof HttpMediaTypeNotAcceptableException subEx) {
- return handleHttpMediaTypeNotAcceptable(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);
- }
- else if (ex instanceof MissingPathVariableException subEx) {
- return handleMissingPathVariable(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);
- }
- else if (ex instanceof MissingServletRequestParameterException subEx) {
- return handleMissingServletRequestParameter(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);
- }
- else if (ex instanceof MissingServletRequestPartException subEx) {
- return handleMissingServletRequestPart(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);
- }
- else if (ex instanceof ServletRequestBindingException subEx) {
- return handleServletRequestBindingException(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);
- }
- else if (ex instanceof MethodArgumentNotValidException subEx) {
- return handleMethodArgumentNotValid(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);
- }
- else if (ex instanceof NoHandlerFoundException subEx) {
- return handleNoHandlerFoundException(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);
- }
- else if (ex instanceof AsyncRequestTimeoutException subEx) {
- return handleAsyncRequestTimeoutException(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);
- }
- else {
- // Another ErrorResponseException
- return handleExceptionInternal(ex, null, errorEx.getHeaders(), errorEx.getStatusCode(), request);
- }
+ if (ex instanceof HttpRequestMethodNotSupportedException subEx) {
+ return handleHttpRequestMethodNotSupported(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);
+ }
+ else if (ex instanceof HttpMediaTypeNotSupportedException subEx) {
+ return handleHttpMediaTypeNotSupported(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);
+ }
+ else if (ex instanceof HttpMediaTypeNotAcceptableException subEx) {
+ return handleHttpMediaTypeNotAcceptable(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);
+ }
+ else if (ex instanceof MissingPathVariableException subEx) {
+ return handleMissingPathVariable(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);
+ }
+ else if (ex instanceof MissingServletRequestParameterException subEx) {
+ return handleMissingServletRequestParameter(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);
+ }
+ else if (ex instanceof MissingServletRequestPartException subEx) {
+ return handleMissingServletRequestPart(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);
+ }
+ else if (ex instanceof ServletRequestBindingException subEx) {
+ return handleServletRequestBindingException(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);
+ }
+ else if (ex instanceof MethodArgumentNotValidException subEx) {
+ return handleMethodArgumentNotValid(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);
+ }
+ else if (ex instanceof NoHandlerFoundException subEx) {
+ return handleNoHandlerFoundException(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);
+ }
+ else if (ex instanceof AsyncRequestTimeoutException subEx) {
+ return handleAsyncRequestTimeoutException(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);
+ }
+ else if (ex instanceof ErrorResponse errorEx) {
+ return handleExceptionInternal(ex, null, errorEx.getHeaders(), errorEx.getStatusCode(), request);
}
- // Other, lower level exceptions
+ // Lower level exceptions, and exceptions used symmetrically on client and server
- if (ex instanceof ConversionNotSupportedException cnse) {
- return handleConversionNotSupported(cnse, headers, HttpStatus.INTERNAL_SERVER_ERROR, request);
+ if (ex instanceof ConversionNotSupportedException theEx) {
+ return handleConversionNotSupported(theEx, headers, HttpStatus.INTERNAL_SERVER_ERROR, request);
}
- else if (ex instanceof TypeMismatchException tme) {
- return handleTypeMismatch(tme, headers, HttpStatus.BAD_REQUEST, request);
+ else if (ex instanceof TypeMismatchException theEx) {
+ return handleTypeMismatch(theEx, headers, HttpStatus.BAD_REQUEST, request);
}
- else if (ex instanceof HttpMessageNotReadableException hmnre) {
- return handleHttpMessageNotReadable(hmnre, headers, HttpStatus.BAD_REQUEST, request);
+ else if (ex instanceof HttpMessageNotReadableException theEx) {
+ return handleHttpMessageNotReadable(theEx, headers, HttpStatus.BAD_REQUEST, request);
}
- else if (ex instanceof HttpMessageNotWritableException hmnwe) {
- return handleHttpMessageNotWritable(hmnwe, headers, HttpStatus.INTERNAL_SERVER_ERROR, request);
+ else if (ex instanceof HttpMessageNotWritableException theEx) {
+ return handleHttpMessageNotWritable(theEx, headers, HttpStatus.INTERNAL_SERVER_ERROR, request);
}
- else if (ex instanceof BindException be) {
- return handleBindException(be, headers, HttpStatus.BAD_REQUEST, request);
+ else if (ex instanceof BindException theEx) {
+ return handleBindException(theEx, headers, HttpStatus.BAD_REQUEST, request);
}
else {
// Unknown exception, typically a wrapper with a common MVC exception as cause
@@ -187,13 +174,14 @@ public abstract class ResponseEntityExceptionHandler {
}
/**
- * Customize the response for HttpRequestMethodNotSupportedException.
- * This method logs a warning, and delegates to {@link #handleExceptionInternal}.
- * @param ex the exception
- * @param headers the headers to be written to the response
- * @param status the selected response status
+ * Customize the handling of {@link HttpRequestMethodNotSupportedException}.
+ *
This method logs a warning and delegates to {@link #handleExceptionInternal}.
+ * @param ex the exception to handle
+ * @param headers the headers to use for the response
+ * @param status the status code to use for the response
* @param request the current request
- * @return {@code ResponseEntity} or {@code null} if response is committed
+ * @return a {@code ResponseEntity} for the response to use, possibly
+ * {@code null} when the response is already committed
*/
@Nullable
protected ResponseEntity handleHttpRequestMethodNotSupported(
@@ -204,13 +192,14 @@ public abstract class ResponseEntityExceptionHandler {
}
/**
- * Customize the response for HttpMediaTypeNotSupportedException.
+ * Customize the handling of {@link HttpMediaTypeNotSupportedException}.
* This method delegates to {@link #handleExceptionInternal}.
- * @param ex the exception
- * @param headers the headers to be written to the response
- * @param status the selected response status
+ * @param ex the exception to handle
+ * @param headers the headers to use for the response
+ * @param status the status code to use for the response
* @param request the current request
- * @return {@code ResponseEntity} or {@code null} if response is committed
+ * @return a {@code ResponseEntity} for the response to use, possibly
+ * {@code null} when the response is already committed
*/
@Nullable
protected ResponseEntity handleHttpMediaTypeNotSupported(
@@ -220,13 +209,14 @@ public abstract class ResponseEntityExceptionHandler {
}
/**
- * Customize the response for HttpMediaTypeNotAcceptableException.
+ * Customize the handling of {@link HttpMediaTypeNotAcceptableException}.
* This method delegates to {@link #handleExceptionInternal}.
- * @param ex the exception
- * @param headers the headers to be written to the response
- * @param status the selected response status
+ * @param ex the exception to handle
+ * @param headers the headers to use for the response
+ * @param status the status code to use for the response
* @param request the current request
- * @return {@code ResponseEntity} or {@code null} if response is committed
+ * @return a {@code ResponseEntity} for the response to use, possibly
+ * {@code null} when the response is already committed
*/
@Nullable
protected ResponseEntity handleHttpMediaTypeNotAcceptable(
@@ -236,13 +226,14 @@ public abstract class ResponseEntityExceptionHandler {
}
/**
- * Customize the response for MissingPathVariableException.
+ * Customize the handling of {@link MissingPathVariableException}.
* This method delegates to {@link #handleExceptionInternal}.
- * @param ex the exception
- * @param headers the headers to be written to the response
- * @param status the selected response status
+ * @param ex the exception to handle
+ * @param headers the headers to use for the response
+ * @param status the status code to use for the response
* @param request the current request
- * @return {@code ResponseEntity} or {@code null} if response is committed
+ * @return a {@code ResponseEntity} for the response to use, possibly
+ * {@code null} when the response is already committed
* @since 4.2
*/
@Nullable
@@ -253,13 +244,14 @@ public abstract class ResponseEntityExceptionHandler {
}
/**
- * Customize the response for MissingServletRequestParameterException.
+ * Customize the handling of {@link MissingServletRequestParameterException}.
*
This method delegates to {@link #handleExceptionInternal}.
- * @param ex the exception
- * @param headers the headers to be written to the response
- * @param status the selected response status
+ * @param ex the exception to handle
+ * @param headers the headers to use for the response
+ * @param status the status code to use for the response
* @param request the current request
- * @return {@code ResponseEntity} or {@code null} if response is committed
+ * @return a {@code ResponseEntity} for the response to use, possibly
+ * {@code null} when the response is already committed
*/
@Nullable
protected ResponseEntity handleMissingServletRequestParameter(
@@ -269,13 +261,14 @@ public abstract class ResponseEntityExceptionHandler {
}
/**
- * Customize the response for MissingServletRequestPartException.
+ * Customize the handling of {@link MissingServletRequestPartException}.
* This method delegates to {@link #handleExceptionInternal}.
- * @param ex the exception
- * @param headers the headers to be written to the response
- * @param status the selected response status
+ * @param ex the exception to handle
+ * @param headers the headers to use for the response
+ * @param status the status code to use for the response
* @param request the current request
- * @return {@code ResponseEntity} or {@code null} if response is committed
+ * @return a {@code ResponseEntity} for the response to use, possibly
+ * {@code null} when the response is already committed
*/
@Nullable
protected ResponseEntity handleMissingServletRequestPart(
@@ -285,13 +278,14 @@ public abstract class ResponseEntityExceptionHandler {
}
/**
- * Customize the response for ServletRequestBindingException.
+ * Customize the handling of {@link ServletRequestBindingException}.
* This method delegates to {@link #handleExceptionInternal}.
- * @param ex the exception
- * @param headers the headers to be written to the response
- * @param status the selected response status
+ * @param ex the exception to handle
+ * @param headers the headers to use for the response
+ * @param status the status code to use for the response
* @param request the current request
- * @return {@code ResponseEntity} or {@code null} if response is committed
+ * @return a {@code ResponseEntity} for the response to use, possibly
+ * {@code null} when the response is already committed
*/
@Nullable
protected ResponseEntity handleServletRequestBindingException(
@@ -301,13 +295,14 @@ public abstract class ResponseEntityExceptionHandler {
}
/**
- * Customize the response for MethodArgumentNotValidException.
+ * Customize the handling of {@link MethodArgumentNotValidException}.
* This method delegates to {@link #handleExceptionInternal}.
- * @param ex the exception
+ * @param ex the exception to handle
* @param headers the headers to be written to the response
* @param status the selected response status
* @param request the current request
- * @return {@code ResponseEntity} or {@code null} if response is committed
+ * @return a {@code ResponseEntity} for the response to use, possibly
+ * {@code null} when the response is already committed
*/
@Nullable
protected ResponseEntity handleMethodArgumentNotValid(
@@ -317,13 +312,14 @@ public abstract class ResponseEntityExceptionHandler {
}
/**
- * Customize the response for NoHandlerFoundException.
+ * Customize the handling of {@link NoHandlerFoundException}.
* This method delegates to {@link #handleExceptionInternal}.
- * @param ex the exception
- * @param headers the headers to be written to the response
- * @param status the selected response status
+ * @param ex the exception to handle
+ * @param headers the headers to use for the response
+ * @param status the status code to use for the response
* @param request the current request
- * @return {@code ResponseEntity} or {@code null} if response is committed
+ * @return a {@code ResponseEntity} for the response to use, possibly
+ * {@code null} when the response is already committed
* @since 4.0
*/
@Nullable
@@ -334,136 +330,186 @@ public abstract class ResponseEntityExceptionHandler {
}
/**
- * Customize the response for AsyncRequestTimeoutException.
+ * Customize the handling of {@link AsyncRequestTimeoutException}.
*
This method delegates to {@link #handleExceptionInternal}.
- * @param ex the exception
- * @param headers the headers to be written to the response
- * @param status the selected response status
- * @param webRequest the current request
- * @return {@code ResponseEntity} or {@code null} if response is committed
+ * @param ex the exception to handle
+ * @param headers the headers to use for the response
+ * @param status the status code to use for the response
+ * @param request the current request
+ * @return a {@code ResponseEntity} for the response to use, possibly
+ * {@code null} when the response is already committed
* @since 4.2.8
*/
@Nullable
protected ResponseEntity handleAsyncRequestTimeoutException(
- AsyncRequestTimeoutException ex, HttpHeaders headers, HttpStatusCode status, WebRequest webRequest) {
+ AsyncRequestTimeoutException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
- return handleExceptionInternal(ex, null, headers, status, webRequest);
+ return handleExceptionInternal(ex, null, headers, status, request);
}
/**
- * Customize the response for ConversionNotSupportedException.
- * This method delegates to {@link #handleExceptionInternal}.
- * @param ex the exception
- * @param headers the headers to be written to the response
- * @param status the selected response status
+ * Customize the handling of {@link ConversionNotSupportedException}.
+ *
By default this method creates a {@link ProblemDetail} with the status
+ * and a short detail message, and then delegates to
+ * {@link #handleExceptionInternal}.
+ * @param ex the exception to handle
+ * @param headers the headers to use for the response
+ * @param status the status code to use for the response
* @param request the current request
- * @return {@code ResponseEntity} or {@code null} if response is committed
+ * @return a {@code ResponseEntity} for the response to use, possibly
+ * {@code null} when the response is already committed
*/
@Nullable
protected ResponseEntity handleConversionNotSupported(
ConversionNotSupportedException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
- return handleExceptionInternal(ex, null, headers, status, request);
+ ProblemDetail body = ProblemDetail.forStatusAndDetail(status,
+ "Failed to convert '" + ex.getPropertyName() + "' with value: '" + ex.getValue() + "'");
+
+ return handleExceptionInternal(ex, body, headers, status, request);
}
/**
- * Customize the response for TypeMismatchException.
- * This method delegates to {@link #handleExceptionInternal}.
- * @param ex the exception
- * @param headers the headers to be written to the response
- * @param status the selected response status
+ * Customize the handling of {@link TypeMismatchException}.
+ *
By default this method creates a {@link ProblemDetail} with the status
+ * and a short detail message, and then delegates to
+ * {@link #handleExceptionInternal}.
+ * @param ex the exception to handle
+ * @param headers the headers to use for the response
+ * @param status the status code to use for the response
* @param request the current request
- * @return {@code ResponseEntity} or {@code null} if response is committed
+ * @return a {@code ResponseEntity} for the response to use, possibly
+ * {@code null} when the response is already committed
*/
@Nullable
protected ResponseEntity handleTypeMismatch(
TypeMismatchException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
- return handleExceptionInternal(ex, null, headers, status, request);
+ ProblemDetail body = ProblemDetail.forStatusAndDetail(status,
+ "Unexpected type for '" + ex.getPropertyName() + "' with value: '" + ex.getValue() + "'");
+
+ return handleExceptionInternal(ex, body, headers, status, request);
}
/**
- * Customize the response for HttpMessageNotReadableException.
- * This method delegates to {@link #handleExceptionInternal}.
- * @param ex the exception
- * @param headers the headers to be written to the response
- * @param status the selected response status
+ * Customize the handling of {@link HttpMessageNotReadableException}.
+ *
By default this method creates a {@link ProblemDetail} with the status
+ * and a short detail message, and then delegates to
+ * {@link #handleExceptionInternal}.
+ * @param ex the exception to handle
+ * @param headers the headers to use for the response
+ * @param status the status code to use for the response
* @param request the current request
- * @return {@code ResponseEntity} or {@code null} if response is committed
+ * @return a {@code ResponseEntity} for the response to use, possibly
+ * {@code null} when the response is already committed
*/
@Nullable
protected ResponseEntity handleHttpMessageNotReadable(
HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
- return handleExceptionInternal(ex, null, headers, status, request);
+ ProblemDetail body = ProblemDetail.forStatusAndDetail(status, "Failed to read request body");
+ return handleExceptionInternal(ex, body, headers, status, request);
}
/**
- * Customize the response for HttpMessageNotWritableException.
- * This method delegates to {@link #handleExceptionInternal}.
- * @param ex the exception
- * @param headers the headers to be written to the response
- * @param status the selected response status
+ * Customize the handling of {@link HttpMessageNotWritableException}.
+ *
By default this method creates a {@link ProblemDetail} with the status
+ * and a short detail message, and then delegates to
+ * {@link #handleExceptionInternal}.
+ * @param ex the exception to handle
+ * @param headers the headers to use for the response
+ * @param status the status code to use for the response
* @param request the current request
- * @return {@code ResponseEntity} or {@code null} if response is committed
+ * @return a {@code ResponseEntity} for the response to use, possibly
+ * {@code null} when the response is already committed
*/
@Nullable
protected ResponseEntity handleHttpMessageNotWritable(
HttpMessageNotWritableException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
- return handleExceptionInternal(ex, null, headers, status, request);
+ ProblemDetail body = ProblemDetail.forStatusAndDetail(status, "Failed to write response body");
+ return handleExceptionInternal(ex, body, headers, status, request);
}
/**
- * Customize the response for BindException.
- * This method delegates to {@link #handleExceptionInternal}.
- * @param ex the exception
- * @param headers the headers to be written to the response
- * @param status the selected response status
+ * Customize the handling of {@link BindException}.
+ *
By default this method creates a {@link ProblemDetail} with the status
+ * and a short detail message, and then delegates to
+ * {@link #handleExceptionInternal}.
+ * @param ex the exception to handle
+ * @param headers the headers to use for the response
+ * @param status the status code to use for the response
* @param request the current request
- * @return {@code ResponseEntity} or {@code null} if response is committed
+ * @return a {@code ResponseEntity} for the response to use, possibly
+ * {@code null} when the response is already committed
*/
@Nullable
protected ResponseEntity handleBindException(
BindException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
- return handleExceptionInternal(ex, null, headers, status, request);
+ ProblemDetail body = ProblemDetail.forStatusAndDetail(status, "Failed to bind request");
+ return handleExceptionInternal(ex, body, headers, status, request);
}
/**
- * A single place to customize the response body of all exception types.
- * The default implementation sets the {@link WebUtils#ERROR_EXCEPTION_ATTRIBUTE}
- * request attribute and creates a {@link ResponseEntity} from the given
- * body, headers, and status.
- * @param ex the exception
- * @param body the body for the response
- * @param headers the headers for the response
- * @param statusCode the response status
- * @param webRequest the current request
- * @return {@code ResponseEntity} or {@code null} if response is committed
+ * Internal handler method that all others in this class delegate to, for
+ * common handling, and for the creation of a {@link ResponseEntity}.
+ *
The default implementation does the following:
+ *
+ * return {@code null} if response is already committed
+ * set the {@code "jakarta.servlet.error.exception"} request attribute
+ * if the response status is 500 (INTERNAL_SERVER_ERROR).
+ * extract the {@link ErrorResponse#getBody() body} from
+ * {@link ErrorResponse} exceptions, if the {@code body} is {@code null}.
+ *
+ * @param ex the exception to handle
+ * @param body the body to use for the response
+ * @param headers the headers to use for the response
+ * @param statusCode the status code to use for the response
+ * @param request the current request
+ * @return a {@code ResponseEntity} for the response to use, possibly
+ * {@code null} when the response is already committed
*/
@Nullable
protected ResponseEntity handleExceptionInternal(
- Exception ex, @Nullable Object body, HttpHeaders headers, HttpStatusCode statusCode, WebRequest webRequest) {
+ Exception ex, @Nullable Object body, HttpHeaders headers, HttpStatusCode statusCode, WebRequest request) {
- if (webRequest instanceof ServletWebRequest servletWebRequest) {
+ if (request instanceof ServletWebRequest servletWebRequest) {
HttpServletResponse response = servletWebRequest.getResponse();
if (response != null && response.isCommitted()) {
if (logger.isWarnEnabled()) {
- logger.warn("Ignoring exception, response committed. : " + ex);
+ logger.warn("Response already committed. Ignoring: " + ex);
}
return null;
}
}
- if (HttpStatus.INTERNAL_SERVER_ERROR.equals(statusCode)) {
- webRequest.setAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE, ex, WebRequest.SCOPE_REQUEST);
+ if (statusCode.equals(HttpStatus.INTERNAL_SERVER_ERROR)) {
+ request.setAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE, ex, WebRequest.SCOPE_REQUEST);
}
if (body == null && ex instanceof ErrorResponse errorResponse) {
body = errorResponse.getBody();
}
+ return createResponseEntity(body, headers, statusCode, request);
+ }
+
+ /**
+ * Create the {@link ResponseEntity} to use from the given body, headers,
+ * and statusCode. Subclasses can override this method to inspect and possibly
+ * modify the body, headers, or statusCode, e.g. to re-create an instance of
+ * {@link ProblemDetail} as an extension of {@link ProblemDetail}.
+ * @param body the body to use for the response
+ * @param headers the headers to use for the response
+ * @param statusCode the status code to use for the response
+ * @param request the current request
+ * @return the {@code ResponseEntity} instance to use
+ * @since 6.0
+ */
+ protected ResponseEntity createResponseEntity(
+ @Nullable Object body, HttpHeaders headers, HttpStatusCode statusCode, WebRequest request) {
+
return new ResponseEntity<>(body, headers, statusCode);
}