From cf2b1020f4251e0f2aee30d79c916d5d204e7e6b Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Tue, 11 Oct 2022 15:10:30 +0100 Subject: [PATCH] Update table of supported controller method return types Closes gh-28814 --- .../web/ErrorResponseException.java | 2 +- .../ResponseEntityExceptionHandler.java | 39 ++++++++++++++----- src/docs/asciidoc/web/webmvc.adoc | 19 +++++++++ 3 files changed, 49 insertions(+), 11 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/ErrorResponseException.java b/spring-web/src/main/java/org/springframework/web/ErrorResponseException.java index b4ec54e24b0..febea11a70c 100644 --- a/spring-web/src/main/java/org/springframework/web/ErrorResponseException.java +++ b/spring-web/src/main/java/org/springframework/web/ErrorResponseException.java @@ -80,7 +80,7 @@ public class ErrorResponseException extends NestedRuntimeException implements Er * resolve the detail message with. * @since 6.0 */ - protected ErrorResponseException( + public ErrorResponseException( HttpStatusCode status, ProblemDetail body, @Nullable Throwable cause, @Nullable String messageDetailCode, @Nullable Object[] messageDetailArguments) { 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 1da0808ee4c..a81798b4648 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 @@ -380,7 +380,8 @@ public abstract class ResponseEntityExceptionHandler implements MessageSourceAwa /** * 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 + * and a short detail message, and also looks up an override for the detail + * via {@link MessageSource}, before delegating to * {@link #handleExceptionInternal}. * @param ex the exception to handle * @param headers the headers to use for the response @@ -393,8 +394,10 @@ public abstract class ResponseEntityExceptionHandler implements MessageSourceAwa protected ResponseEntity handleConversionNotSupported( ConversionNotSupportedException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) { - ProblemDetail body = ProblemDetail.forStatusAndDetail(status, - "Failed to convert '" + ex.getPropertyName() + "' with value: '" + ex.getValue() + "'"); + Object[] args = {ex.getPropertyName(), ex.getValue()}; + + ProblemDetail body = resolveDetailViaMessageSource( + status, args, "Failed to convert '" + args[0] + "' with value: '" + args[1] + "'"); return handleExceptionInternal(ex, body, headers, status, request); } @@ -402,7 +405,8 @@ public abstract class ResponseEntityExceptionHandler implements MessageSourceAwa /** * 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 + * and a short detail message, and also looks up an override for the detail + * via {@link MessageSource}, before delegating to * {@link #handleExceptionInternal}. * @param ex the exception to handle * @param headers the headers to use for the response @@ -415,8 +419,10 @@ public abstract class ResponseEntityExceptionHandler implements MessageSourceAwa protected ResponseEntity handleTypeMismatch( TypeMismatchException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) { - ProblemDetail body = ProblemDetail.forStatusAndDetail(status, - "Unexpected type for '" + ex.getPropertyName() + "' with value: '" + ex.getValue() + "'"); + Object[] args = {ex.getPropertyName(), ex.getValue()}; + + ProblemDetail body = resolveDetailViaMessageSource( + status, args, "Failed to convert '" + args[0] + "' with value: '" + args[1] + "'"); return handleExceptionInternal(ex, body, headers, status, request); } @@ -424,7 +430,8 @@ public abstract class ResponseEntityExceptionHandler implements MessageSourceAwa /** * 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 + * and a short detail message, and also looks up an override for the detail + * via {@link MessageSource}, before delegating to * {@link #handleExceptionInternal}. * @param ex the exception to handle * @param headers the headers to use for the response @@ -437,14 +444,15 @@ public abstract class ResponseEntityExceptionHandler implements MessageSourceAwa protected ResponseEntity handleHttpMessageNotReadable( HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) { - ProblemDetail body = ProblemDetail.forStatusAndDetail(status, "Failed to read request body"); + ProblemDetail body = resolveDetailViaMessageSource(status, null, "Failed to read request"); return handleExceptionInternal(ex, body, headers, status, request); } /** * 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 + * and a short detail message, and also looks up an override for the detail + * via {@link MessageSource}, before delegating to * {@link #handleExceptionInternal}. * @param ex the exception to handle * @param headers the headers to use for the response @@ -457,7 +465,7 @@ public abstract class ResponseEntityExceptionHandler implements MessageSourceAwa protected ResponseEntity handleHttpMessageNotWritable( HttpMessageNotWritableException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) { - ProblemDetail body = ProblemDetail.forStatusAndDetail(status, "Failed to write response body"); + ProblemDetail body = resolveDetailViaMessageSource(status, null, "Failed to write request"); return handleExceptionInternal(ex, body, headers, status, request); } @@ -528,6 +536,17 @@ public abstract class ResponseEntityExceptionHandler implements MessageSourceAwa return createResponseEntity(body, headers, statusCode, request); } + // For non-Web exceptions + private ProblemDetail resolveDetailViaMessageSource( + HttpStatusCode status, @Nullable Object[] arguments, String defaultDetail) { + + ProblemDetail body = ProblemDetail.forStatusAndDetail(status, defaultDetail); + ErrorResponseException errorResponseEx = new ErrorResponseException(status, body, null, null, arguments); + body = resolveDetailViaMessageSource(errorResponseEx); + return body; + } + + // For ErrorResponse exceptions private ProblemDetail resolveDetailViaMessageSource(ErrorResponse response) { ProblemDetail body = response.getBody(); if (this.messageSource != null) { diff --git a/src/docs/asciidoc/web/webmvc.adoc b/src/docs/asciidoc/web/webmvc.adoc index db505328b25..9d3c211214f 100644 --- a/src/docs/asciidoc/web/webmvc.adoc +++ b/src/docs/asciidoc/web/webmvc.adoc @@ -4960,6 +4960,9 @@ The actual message code value is parameterized with placeholders, e.g. `"HTTP method {0} not supported"` to be expanded from the arguments. - `ResponseEntityExceptionHandler` uses the message code and the message arguments to resolve the problem "detail" field. +- Lower level exceptions that cannot implement `ErrorResponse`, e.g. `TypeMismatchException`, +have their problem detail, including message code and arguments set in +`ResponseEntityExceptionHandler`. Message codes default to "problemDetail." + the fully qualified exception class name. Some exceptions may expose additional message codes in which case a suffix is added to @@ -4975,6 +4978,10 @@ MVC exceptions: | (default) | +| `ConversionNotSupportedException` +| (default) +| `{0}` property name, `{1}` property value + | `HttpMediaTypeNotAcceptableException` | (default) | `{0}` list of supported media types @@ -4991,6 +4998,14 @@ MVC exceptions: | (default) + ".parseError" | +| `HttpMessageNotReadableException` +| (default) +| + +| `HttpMessageNotWritableException` +| (default) +| + | `HttpRequestMethodNotSupportedException` | (default) | `{0}` the current HTTP method, `{1}` the list of supported HTTP methods @@ -5029,6 +5044,10 @@ MVC exceptions: | (default) | +| `TypeMismatchException` +| (default) +| `{0}` property name, `{1}` property value + | `UnsatisfiedServletRequestParameterException` | (default) | `{0}` the list of parameter conditions