From 7112efee1bef87d401be3a90335c790fb96f6612 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Mon, 25 Aug 2025 14:23:13 +0200 Subject: [PATCH] Align HttpStatus with RFC9110 This commit updates the `HttpStatus` enum with the latest changes in RFC9110: * deprecate "413 Payload Too Large" in favor of "413 Content Too Large" * deprecate "418 I'm a teapot" as it was meant as a joke and is now marked as unused * Introduce new "421 Misdirected Request" * deprecate "422 Unprocessable Entity" in favor of "422 Unprocessable Content" * deprecate "509 Bandwidth Limit Exceeded" as it's now unassigned * deprecate "510 Not Extended" as it's now marked as "historic" The relevant exceptions, test matchers and more have been updated as a result. Closes gh-32870 --- .../servlet/result/StatusResultMatchers.java | 40 ++++- .../servlet/result/StatusResultMatchersDsl.kt | 35 +++++ .../org/springframework/http/HttpStatus.java | 127 +++++++++------ .../springframework/http/ResponseEntity.java | 12 ++ .../web/client/HttpClientErrorException.java | 23 +++ .../MaxUploadSizeExceededException.java | 4 +- .../web/server/ContentTooLargeException.java | 37 +++++ .../web/server/PayloadTooLargeException.java | 2 + .../springframework/http/HttpStatusTests.java | 146 +++++++++--------- .../http/ResponseEntityTests.java | 10 ++ .../client/WebClientResponseException.java | 18 +++ .../function/server/ServerResponse.java | 11 ++ ...AbstractMessageReaderArgumentResolver.java | 4 +- .../function/server/CoRouterFunctionDsl.kt | 7 + .../function/server/RouterFunctionDsl.kt | 11 ++ .../MessageReaderArgumentResolverTests.java | 4 +- .../ResponseEntityExceptionHandlerTests.java | 1 + .../web/servlet/function/ServerResponse.java | 11 ++ .../web/servlet/function/RouterFunctionDsl.kt | 7 + 19 files changed, 385 insertions(+), 125 deletions(-) create mode 100644 spring-web/src/main/java/org/springframework/web/server/ContentTooLargeException.java diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/result/StatusResultMatchers.java b/spring-test/src/main/java/org/springframework/test/web/servlet/result/StatusResultMatchers.java index caa05a88c76..8aa53769ea5 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/result/StatusResultMatchers.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/result/StatusResultMatchers.java @@ -141,7 +141,9 @@ public class StatusResultMatchers { /** * Assert the response status code is {@code HttpStatus.PROCESSING} (102). + * @deprecated since 7.0, removed from WebDAV specification */ + @Deprecated(since = "7.0") public ResultMatcher isProcessing() { return matcher(HttpStatus.PROCESSING); } @@ -364,10 +366,20 @@ public class StatusResultMatchers { return matcher(HttpStatus.PRECONDITION_FAILED); } + /** + * Assert the response status code is {@code HttpStatus.CONTENT_TOO_LARGE} (413). + * @since 7.0 + */ + public ResultMatcher isContentTooLarge() { + return matcher(HttpStatus.CONTENT_TOO_LARGE); + } + /** * Assert the response status code is {@code HttpStatus.PAYLOAD_TOO_LARGE} (413). * @since 4.1 + * @deprecated since 7.0 in favor of {@link #isContentTooLarge()} */ + @Deprecated(since = "7.0") public ResultMatcher isPayloadTooLarge() { return matcher(HttpStatus.PAYLOAD_TOO_LARGE); } @@ -403,14 +415,34 @@ public class StatusResultMatchers { /** * Assert the response status code is {@code HttpStatus.I_AM_A_TEAPOT} (418). + * @deprecated since 7.0, this was marked as unused in RFC 9110 */ + @Deprecated(since = "7.0") public ResultMatcher isIAmATeapot() { - return matcher(HttpStatus.valueOf(418)); + return matcher(HttpStatus.I_AM_A_TEAPOT); + } + + /** + * Assert the response status code is {@code HttpStatus.MISDIRECTED_REQUEST} (421). + * @since 7.0 + */ + public ResultMatcher isMisdirectedRequest() { + return matcher(HttpStatus.MISDIRECTED_REQUEST); + } + + /** + * Assert the response status code is {@code HttpStatus.UNPROCESSABLE_CONTENT} (422). + * @since 7.0 + */ + public ResultMatcher isUnprocessableContent() { + return matcher(HttpStatus.UNPROCESSABLE_CONTENT); } /** * Assert the response status code is {@code HttpStatus.UNPROCESSABLE_ENTITY} (422). + * @deprecated since 7.0 in favor of {@link #isUnprocessableContent()} */ + @Deprecated(since = "7.0") public ResultMatcher isUnprocessableEntity() { return matcher(HttpStatus.UNPROCESSABLE_ENTITY); } @@ -538,14 +570,18 @@ public class StatusResultMatchers { /** * Assert the response status code is {@code HttpStatus.BANDWIDTH_LIMIT_EXCEEDED} (509). + * @deprecated since 7.0, since this is now unassigned */ + @Deprecated(since = "7.0") public ResultMatcher isBandwidthLimitExceeded() { - return matcher(HttpStatus.valueOf(509)); + return matcher(HttpStatus.BANDWIDTH_LIMIT_EXCEEDED); } /** * Assert the response status code is {@code HttpStatus.NOT_EXTENDED} (510). + * @deprecated since 7.0, this is now marked as "historic" and not endorsed by a standards body. */ + @Deprecated(since = "7.0") public ResultMatcher isNotExtended() { return matcher(HttpStatus.NOT_EXTENDED); } diff --git a/spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/StatusResultMatchersDsl.kt b/spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/StatusResultMatchersDsl.kt index f92f8571180..0233fbf8077 100644 --- a/spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/StatusResultMatchersDsl.kt +++ b/spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/StatusResultMatchersDsl.kt @@ -110,6 +110,8 @@ class StatusResultMatchersDsl internal constructor (private val actions: ResultA /** * @see StatusResultMatchers.isProcessing */ + @Deprecated("Removed from WebDAV specification RFC 4918") + @Suppress("DEPRECATION") fun isProcessing() { actions.andExpect(matchers.isProcessing()) } @@ -325,9 +327,18 @@ class StatusResultMatchersDsl internal constructor (private val actions: ResultA actions.andExpect(matchers.isPreconditionFailed()) } + /** + * @see StatusResultMatchers.isContentTooLarge + */ + fun isContentTooLarge() { + actions.andExpect(matchers.isContentTooLarge()) + } + /** * @see StatusResultMatchers.isPayloadTooLarge */ + @Deprecated("Use Content Too Large instead", replaceWith = ReplaceWith("isContentTooLarge()")) + @Suppress("DEPRECATION") fun isPayloadTooLarge() { actions.andExpect(matchers.isPayloadTooLarge()) } @@ -363,13 +374,33 @@ class StatusResultMatchersDsl internal constructor (private val actions: ResultA /** * @see StatusResultMatchers.isIAmATeapot */ + @Deprecated("Marked as unused in RFC 9110") + @Suppress("DEPRECATION") fun isIAmATeapot() { actions.andExpect(matchers.isIAmATeapot()) } + /** + * @see StatusResultMatchers.isMisdirectedRequest + * @since 7.0 + */ + fun isMisdirectedRequest() { + actions.andExpect(matchers.isMisdirectedRequest()) + } + + /** + * @see StatusResultMatchers.isUnprocessableContent + * @since 7.0 + */ + fun isUnprocessableContent() { + actions.andExpect(matchers.isUnprocessableContent()) + } + /** * @see StatusResultMatchers.isUnprocessableEntity */ + @Deprecated("Use UnprocessableContent instead.", ReplaceWith("isUnprocessableContent()")) + @Suppress("DEPRECATION") fun isUnprocessableEntity() { actions.andExpect(matchers.isUnprocessableEntity()) } @@ -496,6 +527,8 @@ class StatusResultMatchersDsl internal constructor (private val actions: ResultA /** * @see StatusResultMatchers.isBandwidthLimitExceeded */ + @Deprecated("This is now unassigned") + @Suppress("DEPRECATION") fun isBandwidthLimitExceeded() { actions.andExpect(matchers.isBandwidthLimitExceeded()) } @@ -503,6 +536,8 @@ class StatusResultMatchersDsl internal constructor (private val actions: ResultA /** * @see StatusResultMatchers.isNotExtended */ + @Deprecated("This is marked as 'historic' and is not endorsed by a standards body") + @Suppress("DEPRECATION") fun isNotExtended() { actions.andExpect(matchers.isNotExtended()) } diff --git a/spring-web/src/main/java/org/springframework/http/HttpStatus.java b/spring-web/src/main/java/org/springframework/http/HttpStatus.java index 796a01020f8..c5f42dabd9f 100644 --- a/spring-web/src/main/java/org/springframework/http/HttpStatus.java +++ b/spring-web/src/main/java/org/springframework/http/HttpStatus.java @@ -37,18 +37,20 @@ public enum HttpStatus implements HttpStatusCode { /** * {@code 100 Continue}. - * @see HTTP/1.1: Semantics and Content, section 6.2.1 + * @see HTTP Semantics, section 15.2.1 */ CONTINUE(100, Series.INFORMATIONAL, "Continue"), /** * {@code 101 Switching Protocols}. - * @see HTTP/1.1: Semantics and Content, section 6.2.2 + * @see HTTP Semantics, section 15.2.2 */ SWITCHING_PROTOCOLS(101, Series.INFORMATIONAL, "Switching Protocols"), /** * {@code 102 Processing}. * @see WebDAV + * @deprecated since 7.0, removed from WebDAV specification */ + @Deprecated(since = "7.0") PROCESSING(102, Series.INFORMATIONAL, "Processing"), /** * {@code 103 Early Hints}. @@ -61,42 +63,42 @@ public enum HttpStatus implements HttpStatusCode { /** * {@code 200 OK}. - * @see HTTP/1.1: Semantics and Content, section 6.3.1 + * @see HTTP Semantics, section 15.3.1 */ OK(200, Series.SUCCESSFUL, "OK"), /** * {@code 201 Created}. - * @see HTTP/1.1: Semantics and Content, section 6.3.2 + * @see HTTP Semantics, section 15.3.2 */ CREATED(201, Series.SUCCESSFUL, "Created"), /** * {@code 202 Accepted}. - * @see HTTP/1.1: Semantics and Content, section 6.3.3 + * @see HTTP Semantics, section 15.3.3 */ ACCEPTED(202, Series.SUCCESSFUL, "Accepted"), /** * {@code 203 Non-Authoritative Information}. - * @see HTTP/1.1: Semantics and Content, section 6.3.4 + * @see HTTP Semantics, section 15.3.4 */ NON_AUTHORITATIVE_INFORMATION(203, Series.SUCCESSFUL, "Non-Authoritative Information"), /** * {@code 204 No Content}. - * @see HTTP/1.1: Semantics and Content, section 6.3.5 + * @see HTTP Semantics, section 15.3.5 */ NO_CONTENT(204, Series.SUCCESSFUL, "No Content"), /** * {@code 205 Reset Content}. - * @see HTTP/1.1: Semantics and Content, section 6.3.6 + * @see HTTP Semantics, section 15.3.6 */ RESET_CONTENT(205, Series.SUCCESSFUL, "Reset Content"), /** * {@code 206 Partial Content}. - * @see HTTP/1.1: Range Requests, section 4.1 + * @see HTTP/1.1: Range Requests, section 4.1 */ PARTIAL_CONTENT(206, Series.SUCCESSFUL, "Partial Content"), /** * {@code 207 Multi-Status}. - * @see WebDAV + * @see WebDAV */ MULTI_STATUS(207, Series.SUCCESSFUL, "Multi-Status"), /** @@ -114,37 +116,37 @@ public enum HttpStatus implements HttpStatusCode { /** * {@code 300 Multiple Choices}. - * @see HTTP/1.1: Semantics and Content, section 6.4.1 + * @see HTTP Semantics, section 15.4.1 */ MULTIPLE_CHOICES(300, Series.REDIRECTION, "Multiple Choices"), /** * {@code 301 Moved Permanently}. - * @see HTTP/1.1: Semantics and Content, section 6.4.2 + * @see HTTP Semantics, section 15.4.2 */ MOVED_PERMANENTLY(301, Series.REDIRECTION, "Moved Permanently"), /** * {@code 302 Found}. - * @see HTTP/1.1: Semantics and Content, section 6.4.3 + * @see HTTP Semantics, section 15.4.3 */ FOUND(302, Series.REDIRECTION, "Found"), /** * {@code 303 See Other}. - * @see HTTP/1.1: Semantics and Content, section 6.4.4 + * @see HTTP Semantics, section 15.4.4 */ SEE_OTHER(303, Series.REDIRECTION, "See Other"), /** * {@code 304 Not Modified}. - * @see HTTP/1.1: Conditional Requests, section 4.1 + * @see HTTP Semantics, section 15.4.5 */ NOT_MODIFIED(304, Series.REDIRECTION, "Not Modified"), /** * {@code 307 Temporary Redirect}. - * @see HTTP/1.1: Semantics and Content, section 6.4.7 + * @see HTTP Semantics, section 15.4.8 */ TEMPORARY_REDIRECT(307, Series.REDIRECTION, "Temporary Redirect"), /** * {@code 308 Permanent Redirect}. - * @see RFC 7238 + * @see HTTP Semantics, section 15.4.9 */ PERMANENT_REDIRECT(308, Series.REDIRECTION, "Permanent Redirect"), @@ -152,70 +154,70 @@ public enum HttpStatus implements HttpStatusCode { /** * {@code 400 Bad Request}. - * @see HTTP/1.1: Semantics and Content, section 6.5.1 + * @see HTTP Semantics, section 15.5.1 */ BAD_REQUEST(400, Series.CLIENT_ERROR, "Bad Request"), /** * {@code 401 Unauthorized}. - * @see HTTP/1.1: Authentication, section 3.1 + * @see HTTP Semantics, section 15.5.2 */ UNAUTHORIZED(401, Series.CLIENT_ERROR, "Unauthorized"), /** * {@code 402 Payment Required}. - * @see HTTP/1.1: Semantics and Content, section 6.5.2 + * @see HTTP Semantics, section 15.5.3 */ PAYMENT_REQUIRED(402, Series.CLIENT_ERROR, "Payment Required"), /** * {@code 403 Forbidden}. - * @see HTTP/1.1: Semantics and Content, section 6.5.3 + * @see HTTP Semantics, section 15.5.4 */ FORBIDDEN(403, Series.CLIENT_ERROR, "Forbidden"), /** * {@code 404 Not Found}. - * @see HTTP/1.1: Semantics and Content, section 6.5.4 + * @see HTTP Semantics, section 15.5.5 */ NOT_FOUND(404, Series.CLIENT_ERROR, "Not Found"), /** * {@code 405 Method Not Allowed}. - * @see HTTP/1.1: Semantics and Content, section 6.5.5 + * @see HTTP Semantics, section 15.5.6 */ METHOD_NOT_ALLOWED(405, Series.CLIENT_ERROR, "Method Not Allowed"), /** * {@code 406 Not Acceptable}. - * @see HTTP/1.1: Semantics and Content, section 6.5.6 + * @see HTTP Semantics, section 15.5.7 */ NOT_ACCEPTABLE(406, Series.CLIENT_ERROR, "Not Acceptable"), /** * {@code 407 Proxy Authentication Required}. - * @see HTTP/1.1: Authentication, section 3.2 + * @see HTTP Semantics, section 15.5.8 */ PROXY_AUTHENTICATION_REQUIRED(407, Series.CLIENT_ERROR, "Proxy Authentication Required"), /** * {@code 408 Request Timeout}. - * @see HTTP/1.1: Semantics and Content, section 6.5.7 + * @see HTTP Semantics, section 15.5.9 */ REQUEST_TIMEOUT(408, Series.CLIENT_ERROR, "Request Timeout"), /** * {@code 409 Conflict}. - * @see HTTP/1.1: Semantics and Content, section 6.5.8 + * @see HTTP Semantics, section 15.5.10 */ CONFLICT(409, Series.CLIENT_ERROR, "Conflict"), /** * {@code 410 Gone}. - * @see - * HTTP/1.1: Semantics and Content, section 6.5.9 + * @see + * HTTP Semantics, section 15.5.11 */ GONE(410, Series.CLIENT_ERROR, "Gone"), /** * {@code 411 Length Required}. - * @see - * HTTP/1.1: Semantics and Content, section 6.5.10 + * @see + * HTTP Semantics, section 15.5.12 */ LENGTH_REQUIRED(411, Series.CLIENT_ERROR, "Length Required"), /** * {@code 412 Precondition failed}. - * @see - * HTTP/1.1: Conditional Requests, section 4.2 + * @see + * HTTP Semantics, section 15.5.13 */ PRECONDITION_FAILED(412, Series.CLIENT_ERROR, "Precondition Failed"), /** @@ -223,41 +225,67 @@ public enum HttpStatus implements HttpStatusCode { * @since 4.1 * @see * HTTP/1.1: Semantics and Content, section 6.5.11 + * @deprecated since 7.0 in favor of {@link #CONTENT_TOO_LARGE} */ + @Deprecated(since = "7.0") PAYLOAD_TOO_LARGE(413, Series.CLIENT_ERROR, "Payload Too Large"), + /** + * {@code 413 Content Too Large}. + * @since 7.0 + * @see + * HTTP Semantics, section 15.5.14 + */ + CONTENT_TOO_LARGE(413, Series.CLIENT_ERROR, "Content Too Large"), /** * {@code 414 URI Too Long}. * @since 4.1 - * @see - * HTTP/1.1: Semantics and Content, section 6.5.12 + * @see + * HTTP Semantics, section 15.5.15 */ URI_TOO_LONG(414, Series.CLIENT_ERROR, "URI Too Long"), /** * {@code 415 Unsupported Media Type}. - * @see - * HTTP/1.1: Semantics and Content, section 6.5.13 + * @see + * HTTP Semantics, section 15.5.16 */ UNSUPPORTED_MEDIA_TYPE(415, Series.CLIENT_ERROR, "Unsupported Media Type"), /** * {@code 416 Requested Range Not Satisfiable}. - * @see HTTP/1.1: Range Requests, section 4.4 + * @see + * HTTP Semantics, section 15.5.17 */ REQUESTED_RANGE_NOT_SATISFIABLE(416, Series.CLIENT_ERROR, "Requested range not satisfiable"), /** * {@code 417 Expectation Failed}. - * @see - * HTTP/1.1: Semantics and Content, section 6.5.14 + * @see + * HTTP Semantics, section 15.5.18 */ EXPECTATION_FAILED(417, Series.CLIENT_ERROR, "Expectation Failed"), /** * {@code 418 I'm a teapot}. * @see HTCPCP/1.0 + * @deprecated since 7.0, marked as unused in RFC 9110 */ + @Deprecated(since = "7.0") I_AM_A_TEAPOT(418, Series.CLIENT_ERROR, "I'm a teapot"), + /** + * {@code 421 Misdirected Request}. + * @see + * HTTP Semantics, section 15.5.20 + */ + MISDIRECTED_REQUEST(421, Series.CLIENT_ERROR, "Misdirected Request"), + /** + * {@code 422 Unprocessable Content}. + * @see + * HTTP Semantics, section 15.5.21 + */ + UNPROCESSABLE_CONTENT(422, Series.CLIENT_ERROR, "Unprocessable Content"), /** * {@code 422 Unprocessable Entity}. * @see WebDAV + * @deprecated since 7.0 in favor of {@link #UNPROCESSABLE_CONTENT} */ + @Deprecated(since = "7.0") UNPROCESSABLE_ENTITY(422, Series.CLIENT_ERROR, "Unprocessable Entity"), /** * {@code 423 Locked}. @@ -277,7 +305,8 @@ public enum HttpStatus implements HttpStatusCode { TOO_EARLY(425, Series.CLIENT_ERROR, "Too Early"), /** * {@code 426 Upgrade Required}. - * @see Upgrading to TLS Within HTTP/1.1 + * @see + * HTTP Semantics, section 15.5.22 */ UPGRADE_REQUIRED(426, Series.CLIENT_ERROR, "Upgrade Required"), /** @@ -307,32 +336,32 @@ public enum HttpStatus implements HttpStatusCode { /** * {@code 500 Internal Server Error}. - * @see HTTP/1.1: Semantics and Content, section 6.6.1 + * @see HTTP Semantics, section 15.6.1 */ INTERNAL_SERVER_ERROR(500, Series.SERVER_ERROR, "Internal Server Error"), /** * {@code 501 Not Implemented}. - * @see HTTP/1.1: Semantics and Content, section 6.6.2 + * @see HTTP Semantics, section 15.6.2 */ NOT_IMPLEMENTED(501, Series.SERVER_ERROR, "Not Implemented"), /** * {@code 502 Bad Gateway}. - * @see HTTP/1.1: Semantics and Content, section 6.6.3 + * @see HTTP Semantics, section 15.6.3 */ BAD_GATEWAY(502, Series.SERVER_ERROR, "Bad Gateway"), /** * {@code 503 Service Unavailable}. - * @see HTTP/1.1: Semantics and Content, section 6.6.4 + * @see HTTP Semantics, section 15.6.4 */ SERVICE_UNAVAILABLE(503, Series.SERVER_ERROR, "Service Unavailable"), /** * {@code 504 Gateway Timeout}. - * @see HTTP/1.1: Semantics and Content, section 6.6.5 + * @see HTTP Semantics, section 15.6.5 */ GATEWAY_TIMEOUT(504, Series.SERVER_ERROR, "Gateway Timeout"), /** * {@code 505 HTTP Version Not Supported}. - * @see HTTP/1.1: Semantics and Content, section 6.6.6 + * @see HTTP Semantics, section 15.6.6 */ HTTP_VERSION_NOT_SUPPORTED(505, Series.SERVER_ERROR, "HTTP Version not supported"), /** @@ -352,12 +381,16 @@ public enum HttpStatus implements HttpStatusCode { LOOP_DETECTED(508, Series.SERVER_ERROR, "Loop Detected"), /** * {@code 509 Bandwidth Limit Exceeded} + * @deprecated since 7.0, this is now unassigned */ + @Deprecated(since = "7.0") BANDWIDTH_LIMIT_EXCEEDED(509, Series.SERVER_ERROR, "Bandwidth Limit Exceeded"), /** * {@code 510 Not Extended} * @see HTTP Extension Framework + * @deprecated since 7.0, this is now marked as "historic" and not endorsed by a standards body. */ + @Deprecated(since = "7.0") NOT_EXTENDED(510, Series.SERVER_ERROR, "Not Extended"), /** * {@code 511 Network Authentication Required}. diff --git a/spring-web/src/main/java/org/springframework/http/ResponseEntity.java b/spring-web/src/main/java/org/springframework/http/ResponseEntity.java index 2b63f835603..9d879cf5526 100644 --- a/spring-web/src/main/java/org/springframework/http/ResponseEntity.java +++ b/spring-web/src/main/java/org/springframework/http/ResponseEntity.java @@ -362,12 +362,24 @@ public class ResponseEntity extends HttpEntity { return status(HttpStatus.NOT_FOUND); } + /** + * Create a builder with an + * {@linkplain HttpStatus#UNPROCESSABLE_CONTENT UNPROCESSABLE_CONTENT} status. + * @return the created builder + * @since 7.0 + */ + public static BodyBuilder unprocessableContent() { + return status(HttpStatus.UNPROCESSABLE_CONTENT); + } + /** * Create a builder with an * {@linkplain HttpStatus#UNPROCESSABLE_ENTITY UNPROCESSABLE_ENTITY} status. * @return the created builder * @since 4.1.3 + * @deprecated since 7.0 in favor of {@link #unprocessableContent()} */ + @Deprecated(since = "7.0") public static BodyBuilder unprocessableEntity() { return status(HttpStatus.UNPROCESSABLE_ENTITY); } diff --git a/spring-web/src/main/java/org/springframework/web/client/HttpClientErrorException.java b/spring-web/src/main/java/org/springframework/web/client/HttpClientErrorException.java index 139592df1cb..56a794d8e37 100644 --- a/spring-web/src/main/java/org/springframework/web/client/HttpClientErrorException.java +++ b/spring-web/src/main/java/org/springframework/web/client/HttpClientErrorException.java @@ -133,6 +133,9 @@ public class HttpClientErrorException extends HttpStatusCodeException { case UNPROCESSABLE_ENTITY -> message != null ? new UnprocessableEntity(message, statusText, headers, body, charset) : new UnprocessableEntity(statusText, headers, body, charset); + case UNPROCESSABLE_CONTENT -> message != null ? + new UnprocessableContent(message, statusText, headers, body, charset) : + new UnprocessableContent(statusText, headers, body, charset); default -> message != null ? new HttpClientErrorException(message, statusCode, statusText, headers, body, charset) : new HttpClientErrorException(statusCode, statusText, headers, body, charset); @@ -307,10 +310,30 @@ public class HttpClientErrorException extends HttpStatusCodeException { } } + /** + * {@link HttpClientErrorException} for status HTTP 422 Unprocessable Content. + * @since 7.0 + */ + @SuppressWarnings("serial") + public static final class UnprocessableContent extends HttpClientErrorException { + + private UnprocessableContent(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { + super(HttpStatus.UNPROCESSABLE_CONTENT, statusText, headers, body, charset); + } + + private UnprocessableContent(String message, String statusText, + HttpHeaders headers, byte[] body, @Nullable Charset charset) { + + super(message, HttpStatus.UNPROCESSABLE_CONTENT, statusText, headers, body, charset); + } + } + /** * {@link HttpClientErrorException} for status HTTP 422 Unprocessable Entity. * @since 5.1 + * @deprecated since 7.0 in favor of {@link UnprocessableContent} */ + @Deprecated(since = "7.0") @SuppressWarnings("serial") public static final class UnprocessableEntity extends HttpClientErrorException { diff --git a/spring-web/src/main/java/org/springframework/web/multipart/MaxUploadSizeExceededException.java b/spring-web/src/main/java/org/springframework/web/multipart/MaxUploadSizeExceededException.java index 90a7368db03..9532943e760 100644 --- a/spring-web/src/main/java/org/springframework/web/multipart/MaxUploadSizeExceededException.java +++ b/spring-web/src/main/java/org/springframework/web/multipart/MaxUploadSizeExceededException.java @@ -35,7 +35,7 @@ import org.springframework.web.ErrorResponse; public class MaxUploadSizeExceededException extends MultipartException implements ErrorResponse { private final ProblemDetail body = - ProblemDetail.forStatusAndDetail(HttpStatus.PAYLOAD_TOO_LARGE, "Maximum upload size exceeded"); + ProblemDetail.forStatusAndDetail(HttpStatus.CONTENT_TOO_LARGE, "Maximum upload size exceeded"); private final long maxUploadSize; @@ -71,7 +71,7 @@ public class MaxUploadSizeExceededException extends MultipartException implement @Override public HttpStatusCode getStatusCode() { - return HttpStatus.PAYLOAD_TOO_LARGE; + return HttpStatus.CONTENT_TOO_LARGE; } @Override diff --git a/spring-web/src/main/java/org/springframework/web/server/ContentTooLargeException.java b/spring-web/src/main/java/org/springframework/web/server/ContentTooLargeException.java new file mode 100644 index 00000000000..744d01406b0 --- /dev/null +++ b/spring-web/src/main/java/org/springframework/web/server/ContentTooLargeException.java @@ -0,0 +1,37 @@ +/* + * Copyright 2002-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.web.server; + +import org.jspecify.annotations.Nullable; + +import org.springframework.http.HttpStatus; + +/** + * Exception for errors that fit response status 413 (Content too large) for use in + * Spring Web applications. + * + * @author Brian Clozel + * @since 7.0 + */ +@SuppressWarnings("serial") +public class ContentTooLargeException extends ResponseStatusException { + + public ContentTooLargeException(@Nullable Throwable cause) { + super(HttpStatus.CONTENT_TOO_LARGE, null, cause); + } + +} diff --git a/spring-web/src/main/java/org/springframework/web/server/PayloadTooLargeException.java b/spring-web/src/main/java/org/springframework/web/server/PayloadTooLargeException.java index 228f7726b5b..b0bf9c054d4 100644 --- a/spring-web/src/main/java/org/springframework/web/server/PayloadTooLargeException.java +++ b/spring-web/src/main/java/org/springframework/web/server/PayloadTooLargeException.java @@ -27,8 +27,10 @@ import org.springframework.http.HttpStatus; * * @author Kim Bosung * @since 6.2 + * @deprecated since 7.0 in favor of {@link ContentTooLargeException} */ @SuppressWarnings("serial") +@Deprecated(since = "7.0") public class PayloadTooLargeException extends ResponseStatusException { public PayloadTooLargeException(@Nullable Throwable cause) { diff --git a/spring-web/src/test/java/org/springframework/http/HttpStatusTests.java b/spring-web/src/test/java/org/springframework/http/HttpStatusTests.java index ee43756b3db..5eabdde97a0 100644 --- a/spring-web/src/test/java/org/springframework/http/HttpStatusTests.java +++ b/spring-web/src/test/java/org/springframework/http/HttpStatusTests.java @@ -16,12 +16,15 @@ package org.springframework.http; -import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; + import static org.assertj.core.api.Assertions.assertThat; /** @@ -29,86 +32,89 @@ import static org.assertj.core.api.Assertions.assertThat; */ class HttpStatusTests { - private final Map statusCodes = new LinkedHashMap<>(); + private final MultiValueMap statusCodes = new LinkedMultiValueMap<>(); @BeforeEach void createStatusCodes() { - statusCodes.put(100, "CONTINUE"); - statusCodes.put(101, "SWITCHING_PROTOCOLS"); - statusCodes.put(102, "PROCESSING"); - statusCodes.put(103, "EARLY_HINTS"); - - statusCodes.put(200, "OK"); - statusCodes.put(201, "CREATED"); - statusCodes.put(202, "ACCEPTED"); - statusCodes.put(203, "NON_AUTHORITATIVE_INFORMATION"); - statusCodes.put(204, "NO_CONTENT"); - statusCodes.put(205, "RESET_CONTENT"); - statusCodes.put(206, "PARTIAL_CONTENT"); - statusCodes.put(207, "MULTI_STATUS"); - statusCodes.put(208, "ALREADY_REPORTED"); - statusCodes.put(226, "IM_USED"); - - statusCodes.put(300, "MULTIPLE_CHOICES"); - statusCodes.put(301, "MOVED_PERMANENTLY"); - statusCodes.put(302, "FOUND"); - statusCodes.put(303, "SEE_OTHER"); - statusCodes.put(304, "NOT_MODIFIED"); - statusCodes.put(307, "TEMPORARY_REDIRECT"); - statusCodes.put(308, "PERMANENT_REDIRECT"); - - statusCodes.put(400, "BAD_REQUEST"); - statusCodes.put(401, "UNAUTHORIZED"); - statusCodes.put(402, "PAYMENT_REQUIRED"); - statusCodes.put(403, "FORBIDDEN"); - statusCodes.put(404, "NOT_FOUND"); - statusCodes.put(405, "METHOD_NOT_ALLOWED"); - statusCodes.put(406, "NOT_ACCEPTABLE"); - statusCodes.put(407, "PROXY_AUTHENTICATION_REQUIRED"); - statusCodes.put(408, "REQUEST_TIMEOUT"); - statusCodes.put(409, "CONFLICT"); - statusCodes.put(410, "GONE"); - statusCodes.put(411, "LENGTH_REQUIRED"); - statusCodes.put(412, "PRECONDITION_FAILED"); - statusCodes.put(413, "PAYLOAD_TOO_LARGE"); - statusCodes.put(414, "URI_TOO_LONG"); - statusCodes.put(415, "UNSUPPORTED_MEDIA_TYPE"); - statusCodes.put(416, "REQUESTED_RANGE_NOT_SATISFIABLE"); - statusCodes.put(417, "EXPECTATION_FAILED"); - statusCodes.put(418, "I_AM_A_TEAPOT"); - statusCodes.put(422, "UNPROCESSABLE_ENTITY"); - statusCodes.put(423, "LOCKED"); - statusCodes.put(424, "FAILED_DEPENDENCY"); - statusCodes.put(425, "TOO_EARLY"); - statusCodes.put(426, "UPGRADE_REQUIRED"); - statusCodes.put(428, "PRECONDITION_REQUIRED"); - statusCodes.put(429, "TOO_MANY_REQUESTS"); - statusCodes.put(431, "REQUEST_HEADER_FIELDS_TOO_LARGE"); - statusCodes.put(451, "UNAVAILABLE_FOR_LEGAL_REASONS"); - - statusCodes.put(500, "INTERNAL_SERVER_ERROR"); - statusCodes.put(501, "NOT_IMPLEMENTED"); - statusCodes.put(502, "BAD_GATEWAY"); - statusCodes.put(503, "SERVICE_UNAVAILABLE"); - statusCodes.put(504, "GATEWAY_TIMEOUT"); - statusCodes.put(505, "HTTP_VERSION_NOT_SUPPORTED"); - statusCodes.put(506, "VARIANT_ALSO_NEGOTIATES"); - statusCodes.put(507, "INSUFFICIENT_STORAGE"); - statusCodes.put(508, "LOOP_DETECTED"); - statusCodes.put(509, "BANDWIDTH_LIMIT_EXCEEDED"); - statusCodes.put(510, "NOT_EXTENDED"); - statusCodes.put(511, "NETWORK_AUTHENTICATION_REQUIRED"); + statusCodes.add(100, "CONTINUE"); + statusCodes.add(101, "SWITCHING_PROTOCOLS"); + statusCodes.add(102, "PROCESSING"); + statusCodes.add(103, "EARLY_HINTS"); + + statusCodes.add(200, "OK"); + statusCodes.add(201, "CREATED"); + statusCodes.add(202, "ACCEPTED"); + statusCodes.add(203, "NON_AUTHORITATIVE_INFORMATION"); + statusCodes.add(204, "NO_CONTENT"); + statusCodes.add(205, "RESET_CONTENT"); + statusCodes.add(206, "PARTIAL_CONTENT"); + statusCodes.add(207, "MULTI_STATUS"); + statusCodes.add(208, "ALREADY_REPORTED"); + statusCodes.add(226, "IM_USED"); + + statusCodes.add(300, "MULTIPLE_CHOICES"); + statusCodes.add(301, "MOVED_PERMANENTLY"); + statusCodes.add(302, "FOUND"); + statusCodes.add(303, "SEE_OTHER"); + statusCodes.add(304, "NOT_MODIFIED"); + statusCodes.add(307, "TEMPORARY_REDIRECT"); + statusCodes.add(308, "PERMANENT_REDIRECT"); + + statusCodes.add(400, "BAD_REQUEST"); + statusCodes.add(401, "UNAUTHORIZED"); + statusCodes.add(402, "PAYMENT_REQUIRED"); + statusCodes.add(403, "FORBIDDEN"); + statusCodes.add(404, "NOT_FOUND"); + statusCodes.add(405, "METHOD_NOT_ALLOWED"); + statusCodes.add(406, "NOT_ACCEPTABLE"); + statusCodes.add(407, "PROXY_AUTHENTICATION_REQUIRED"); + statusCodes.add(408, "REQUEST_TIMEOUT"); + statusCodes.add(409, "CONFLICT"); + statusCodes.add(410, "GONE"); + statusCodes.add(411, "LENGTH_REQUIRED"); + statusCodes.add(412, "PRECONDITION_FAILED"); + statusCodes.add(413, "PAYLOAD_TOO_LARGE"); + statusCodes.add(413, "CONTENT_TOO_LARGE"); + statusCodes.add(414, "URI_TOO_LONG"); + statusCodes.add(415, "UNSUPPORTED_MEDIA_TYPE"); + statusCodes.add(416, "REQUESTED_RANGE_NOT_SATISFIABLE"); + statusCodes.add(417, "EXPECTATION_FAILED"); + statusCodes.add(418, "I_AM_A_TEAPOT"); + statusCodes.add(421, "MISDIRECTED_REQUEST"); + statusCodes.add(422, "UNPROCESSABLE_CONTENT"); + statusCodes.add(422, "UNPROCESSABLE_ENTITY"); + statusCodes.add(423, "LOCKED"); + statusCodes.add(424, "FAILED_DEPENDENCY"); + statusCodes.add(425, "TOO_EARLY"); + statusCodes.add(426, "UPGRADE_REQUIRED"); + statusCodes.add(428, "PRECONDITION_REQUIRED"); + statusCodes.add(429, "TOO_MANY_REQUESTS"); + statusCodes.add(431, "REQUEST_HEADER_FIELDS_TOO_LARGE"); + statusCodes.add(451, "UNAVAILABLE_FOR_LEGAL_REASONS"); + + statusCodes.add(500, "INTERNAL_SERVER_ERROR"); + statusCodes.add(501, "NOT_IMPLEMENTED"); + statusCodes.add(502, "BAD_GATEWAY"); + statusCodes.add(503, "SERVICE_UNAVAILABLE"); + statusCodes.add(504, "GATEWAY_TIMEOUT"); + statusCodes.add(505, "HTTP_VERSION_NOT_SUPPORTED"); + statusCodes.add(506, "VARIANT_ALSO_NEGOTIATES"); + statusCodes.add(507, "INSUFFICIENT_STORAGE"); + statusCodes.add(508, "LOOP_DETECTED"); + statusCodes.add(509, "BANDWIDTH_LIMIT_EXCEEDED"); + statusCodes.add(510, "NOT_EXTENDED"); + statusCodes.add(511, "NETWORK_AUTHENTICATION_REQUIRED"); } @Test void fromMapToEnum() { - for (Map.Entry entry : statusCodes.entrySet()) { + for (Map.Entry> entry : statusCodes.entrySet()) { int value = entry.getKey(); HttpStatus status = HttpStatus.valueOf(value); assertThat(status.value()).as("Invalid value").isEqualTo(value); - assertThat(status.name()).as("Invalid name for [" + value + "]").isEqualTo(entry.getValue()); + assertThat(entry.getValue()).as("Invalid name for [" + value + "]").contains(status.name()); } } @@ -117,7 +123,7 @@ class HttpStatusTests { for (HttpStatus status : HttpStatus.values()) { int code = status.value(); assertThat(statusCodes).as("Map has no value for [" + code + "]").containsKey(code); - assertThat(status.name()).as("Invalid name for [" + code + "]").isEqualTo(statusCodes.get(code)); + assertThat(statusCodes.get(code)).as("Invalid name for [" + code + "]").contains(status.name()); } } diff --git a/spring-web/src/test/java/org/springframework/http/ResponseEntityTests.java b/spring-web/src/test/java/org/springframework/http/ResponseEntityTests.java index dd9141ed38c..e3f27b0b0a6 100644 --- a/spring-web/src/test/java/org/springframework/http/ResponseEntityTests.java +++ b/spring-web/src/test/java/org/springframework/http/ResponseEntityTests.java @@ -166,6 +166,16 @@ class ResponseEntityTests { } @Test + void unprocessableContent() { + ResponseEntity responseEntity = ResponseEntity.unprocessableContent().body("error"); + + assertThat(responseEntity).isNotNull(); + assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.UNPROCESSABLE_CONTENT); + assertThat(responseEntity.getBody()).isEqualTo("error"); + } + + @Test + @SuppressWarnings("deprecate") void unprocessableEntity() { ResponseEntity responseEntity = ResponseEntity.unprocessableEntity().body("error"); diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClientResponseException.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClientResponseException.java index f66bc104b63..c8948475231 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClientResponseException.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClientResponseException.java @@ -464,11 +464,29 @@ public class WebClientResponseException extends WebClientException { } } + /** + * {@link WebClientResponseException} for status HTTP 422 Unprocessable Content. + * @since 7.0 + */ + @SuppressWarnings("serial") + public static class UnprocessableContent extends WebClientResponseException { + + UnprocessableContent( + String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset, + @Nullable HttpRequest request) { + + super(HttpStatus.UNPROCESSABLE_CONTENT.value(), statusText, headers, body, charset, request); + } + } + + /** * {@link WebClientResponseException} for status HTTP 422 Unprocessable Entity. * @since 5.1 + * @deprecated since 7.0 in favor of {@link UnprocessableContent} */ @SuppressWarnings("serial") + @Deprecated(since = "7.0") public static class UnprocessableEntity extends WebClientResponseException { UnprocessableEntity( diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/ServerResponse.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/ServerResponse.java index b9572505981..01ab3fa211e 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/ServerResponse.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/ServerResponse.java @@ -212,11 +212,22 @@ public interface ServerResponse { return status(HttpStatus.NOT_FOUND); } + /** + * Create a builder with an + * {@linkplain HttpStatus#UNPROCESSABLE_CONTENT 422 Unprocessable Content} status. + * @return the created builder + */ + static BodyBuilder unprocessableContent() { + return status(HttpStatus.UNPROCESSABLE_CONTENT); + } + /** * Create a builder with an * {@linkplain HttpStatus#UNPROCESSABLE_ENTITY 422 Unprocessable Entity} status. * @return the created builder + * @deprecated since 7.0 in favor of {@link #unprocessableContent()} */ + @Deprecated(since = "7.0") static BodyBuilder unprocessableEntity() { return status(HttpStatus.UNPROCESSABLE_ENTITY); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java index 1247d3bd685..7d8114d50f7 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java @@ -52,7 +52,7 @@ import org.springframework.web.bind.support.WebExchangeBindException; import org.springframework.web.bind.support.WebExchangeDataBinder; import org.springframework.web.reactive.BindingContext; import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolverSupport; -import org.springframework.web.server.PayloadTooLargeException; +import org.springframework.web.server.ContentTooLargeException; import org.springframework.web.server.ResponseStatusException; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebInputException; @@ -236,7 +236,7 @@ public abstract class AbstractMessageReaderArgumentResolver extends HandlerMetho private Throwable handleReadError(MethodParameter parameter, Throwable ex) { if (ex instanceof DataBufferLimitException) { - return new PayloadTooLargeException(ex); + return new ContentTooLargeException(ex); } if (ex instanceof DecodingException) { return new ServerWebInputException("Failed to read HTTP message", parameter, ex); diff --git a/spring-webflux/src/main/kotlin/org/springframework/web/reactive/function/server/CoRouterFunctionDsl.kt b/spring-webflux/src/main/kotlin/org/springframework/web/reactive/function/server/CoRouterFunctionDsl.kt index 18053e4f414..f022b2b3daf 100644 --- a/spring-webflux/src/main/kotlin/org/springframework/web/reactive/function/server/CoRouterFunctionDsl.kt +++ b/spring-webflux/src/main/kotlin/org/springframework/web/reactive/function/server/CoRouterFunctionDsl.kt @@ -807,9 +807,16 @@ class CoRouterFunctionDsl internal constructor (private val init: (CoRouterFunct */ fun notFound() = ServerResponse.notFound() + /** + * @see ServerResponse.unprocessableContent + */ + fun unprocessableContent() = ServerResponse.unprocessableContent() + /** * @see ServerResponse.unprocessableEntity */ + @Deprecated("Use unprocessable content instead.", ReplaceWith("unprocessableContent()")) + @Suppress("DEPRECATION") fun unprocessableEntity() = ServerResponse.unprocessableEntity() /** diff --git a/spring-webflux/src/main/kotlin/org/springframework/web/reactive/function/server/RouterFunctionDsl.kt b/spring-webflux/src/main/kotlin/org/springframework/web/reactive/function/server/RouterFunctionDsl.kt index c05ef872821..35c77331e9e 100644 --- a/spring-webflux/src/main/kotlin/org/springframework/web/reactive/function/server/RouterFunctionDsl.kt +++ b/spring-webflux/src/main/kotlin/org/springframework/web/reactive/function/server/RouterFunctionDsl.kt @@ -870,12 +870,23 @@ class RouterFunctionDsl internal constructor (private val init: RouterFunctionDs fun notFound(): ServerResponse.HeadersBuilder<*> = ServerResponse.notFound() + /** + * Create a builder with an + * [422 Unprocessable Content][HttpStatus.UNPROCESSABLE_CONTENT] status. + * @return the created builder + * @since 7.0 + */ + fun unprocessableContent(): ServerResponse.BodyBuilder = + ServerResponse.unprocessableContent() + /** * Create a builder with an * [422 Unprocessable Entity][HttpStatus.UNPROCESSABLE_ENTITY] status. * @return the created builder * @since 5.1 */ + @Deprecated("Use unprocessable content instead.", ReplaceWith("unprocessableContent()")) + @Suppress("DEPRECATION") fun unprocessableEntity(): ServerResponse.BodyBuilder = ServerResponse.unprocessableEntity() diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/MessageReaderArgumentResolverTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/MessageReaderArgumentResolverTests.java index e7d833f604f..8ce9bff7012 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/MessageReaderArgumentResolverTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/MessageReaderArgumentResolverTests.java @@ -52,7 +52,7 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.support.ConfigurableWebBindingInitializer; import org.springframework.web.method.HandlerMethod; import org.springframework.web.reactive.BindingContext; -import org.springframework.web.server.PayloadTooLargeException; +import org.springframework.web.server.ContentTooLargeException; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebInputException; import org.springframework.web.server.UnsupportedMediaTypeStatusException; @@ -128,7 +128,7 @@ class MessageReaderArgumentResolverTests { Mono result = (Mono) this.resolver.readBody( param, true, this.bindingContext, exchange).block(); - StepVerifier.create(result).expectError(PayloadTooLargeException.class).verify(); + StepVerifier.create(result).expectError(ContentTooLargeException.class).verify(); } @Test diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseEntityExceptionHandlerTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseEntityExceptionHandlerTests.java index aa4ade257f8..fc64b0e69d7 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseEntityExceptionHandlerTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseEntityExceptionHandlerTests.java @@ -135,6 +135,7 @@ class ResponseEntityExceptionHandlerTests { } @Test + @SuppressWarnings("deprecation") void handleResponseStatusException() { testException(new ResponseStatusException(HttpStatus.BANDWIDTH_LIMIT_EXCEEDED)); } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/function/ServerResponse.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/function/ServerResponse.java index b07176d5518..016ef572c63 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/function/ServerResponse.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/function/ServerResponse.java @@ -211,11 +211,22 @@ public interface ServerResponse { return status(HttpStatus.NOT_FOUND); } + /** + * Create a builder with a + * {@linkplain HttpStatus#UNPROCESSABLE_CONTENT 422 Unprocessable Content} status. + * @return the created builder + */ + static BodyBuilder unprocessableContent() { + return status(HttpStatus.UNPROCESSABLE_CONTENT); + } + /** * Create a builder with a * {@linkplain HttpStatus#UNPROCESSABLE_ENTITY 422 Unprocessable Entity} status. * @return the created builder + * @deprecated since 7.0 in favor of {@link #unprocessableContent()} */ + @Deprecated(since = "7.0") static BodyBuilder unprocessableEntity() { return status(HttpStatus.UNPROCESSABLE_ENTITY); } diff --git a/spring-webmvc/src/main/kotlin/org/springframework/web/servlet/function/RouterFunctionDsl.kt b/spring-webmvc/src/main/kotlin/org/springframework/web/servlet/function/RouterFunctionDsl.kt index 96f08df4db4..6d1474b4a01 100644 --- a/spring-webmvc/src/main/kotlin/org/springframework/web/servlet/function/RouterFunctionDsl.kt +++ b/spring-webmvc/src/main/kotlin/org/springframework/web/servlet/function/RouterFunctionDsl.kt @@ -812,9 +812,16 @@ class RouterFunctionDsl internal constructor (private val init: (RouterFunctionD */ fun notFound() = ServerResponse.notFound() + /** + * @see ServerResponse.unprocessableContent + */ + fun unprocessableContent() = ServerResponse.unprocessableContent() + /** * @see ServerResponse.unprocessableEntity */ + @Deprecated("Use Unprocessable Content instead", ReplaceWith("unprocessableContent()")) + @Suppress("DEPRECATION") fun unprocessableEntity() = ServerResponse.unprocessableEntity() /**