diff --git a/spring-web/src/main/java/org/springframework/web/bind/ServletRequestBindingException.java b/spring-web/src/main/java/org/springframework/web/bind/ServletRequestBindingException.java
index f66ff06412a..cd42052b667 100644
--- a/spring-web/src/main/java/org/springframework/web/bind/ServletRequestBindingException.java
+++ b/spring-web/src/main/java/org/springframework/web/bind/ServletRequestBindingException.java
@@ -25,8 +25,8 @@ import org.springframework.lang.Nullable;
import org.springframework.web.ErrorResponse;
/**
- * Fatal binding exception, thrown when we want to
- * treat binding exceptions as unrecoverable.
+ * Fatal binding exception, thrown when we want to treat binding exceptions
+ * as unrecoverable.
*
*
Extends ServletException for convenient throwing in any Servlet resource
* (such as a Filter), and NestedServletException for proper root cause handling
@@ -38,13 +38,14 @@ import org.springframework.web.ErrorResponse;
@SuppressWarnings("serial")
public class ServletRequestBindingException extends ServletException implements ErrorResponse {
- private final ProblemDetail body = ProblemDetail.forStatus(getStatusCode());
-
private final String messageDetailCode;
@Nullable
private final Object[] messageDetailArguments;
+ @Nullable
+ private ProblemDetail body;
+
/**
* Constructor with a message only.
@@ -108,7 +109,10 @@ public class ServletRequestBindingException extends ServletException implements
}
@Override
- public ProblemDetail getBody() {
+ public synchronized ProblemDetail getBody() {
+ if (this.body == null) {
+ this.body = ProblemDetail.forStatus(getStatusCode());
+ }
return this.body;
}
diff --git a/spring-web/src/test/java/org/springframework/web/ErrorResponseExceptionTests.java b/spring-web/src/test/java/org/springframework/web/ErrorResponseExceptionTests.java
index 177b8b4df1b..f55667bfafc 100644
--- a/spring-web/src/test/java/org/springframework/web/ErrorResponseExceptionTests.java
+++ b/spring-web/src/test/java/org/springframework/web/ErrorResponseExceptionTests.java
@@ -78,7 +78,6 @@ class ErrorResponseExceptionTests {
@Test
void httpMediaTypeNotSupportedException() {
-
List mediaTypes =
Arrays.asList(MediaType.APPLICATION_JSON, MediaType.APPLICATION_CBOR);
@@ -96,7 +95,6 @@ class ErrorResponseExceptionTests {
@Test
void httpMediaTypeNotSupportedExceptionWithParseError() {
-
ErrorResponse ex = new HttpMediaTypeNotSupportedException(
"Could not parse Accept header: Invalid mime type \"foo\": does not contain '/'");
@@ -109,7 +107,6 @@ class ErrorResponseExceptionTests {
@Test
void httpMediaTypeNotAcceptableException() {
-
List mediaTypes = Arrays.asList(MediaType.APPLICATION_JSON, MediaType.APPLICATION_CBOR);
HttpMediaTypeNotAcceptableException ex = new HttpMediaTypeNotAcceptableException(mediaTypes);
@@ -123,7 +120,6 @@ class ErrorResponseExceptionTests {
@Test
void httpMediaTypeNotAcceptableExceptionWithParseError() {
-
ErrorResponse ex = new HttpMediaTypeNotAcceptableException(
"Could not parse Accept header: Invalid mime type \"foo\": does not contain '/'");
@@ -136,7 +132,6 @@ class ErrorResponseExceptionTests {
@Test
void asyncRequestTimeoutException() {
-
ErrorResponse ex = new AsyncRequestTimeoutException();
assertDetailMessageCode(ex, null, null);
@@ -148,7 +143,6 @@ class ErrorResponseExceptionTests {
@Test
void httpRequestMethodNotSupportedException() {
-
HttpRequestMethodNotSupportedException ex =
new HttpRequestMethodNotSupportedException("PUT", Arrays.asList("GET", "POST"));
@@ -162,7 +156,6 @@ class ErrorResponseExceptionTests {
@Test
void missingRequestHeaderException() {
-
MissingRequestHeaderException ex = new MissingRequestHeaderException("Authorization", this.methodParameter);
assertStatus(ex, HttpStatus.BAD_REQUEST);
@@ -174,7 +167,6 @@ class ErrorResponseExceptionTests {
@Test
void missingServletRequestParameterException() {
-
MissingServletRequestParameterException ex = new MissingServletRequestParameterException("query", "String");
assertStatus(ex, HttpStatus.BAD_REQUEST);
@@ -186,10 +178,8 @@ class ErrorResponseExceptionTests {
@Test
void missingMatrixVariableException() {
-
MissingMatrixVariableException ex = new MissingMatrixVariableException("region", this.methodParameter);
-
assertStatus(ex, HttpStatus.BAD_REQUEST);
assertDetail(ex, "Required path parameter 'region' is not present.");
assertDetailMessageCode(ex, null, new Object[] {ex.getVariableName()});
@@ -199,7 +189,6 @@ class ErrorResponseExceptionTests {
@Test
void missingPathVariableException() {
-
MissingPathVariableException ex = new MissingPathVariableException("id", this.methodParameter);
assertStatus(ex, HttpStatus.INTERNAL_SERVER_ERROR);
@@ -210,8 +199,18 @@ class ErrorResponseExceptionTests {
}
@Test
- void missingRequestCookieException() {
+ void missingPathVariableExceptionAfterConversion() {
+ MissingPathVariableException ex = new MissingPathVariableException("id", this.methodParameter, true);
+
+ assertStatus(ex, HttpStatus.BAD_REQUEST);
+ assertDetail(ex, "Required path variable 'id' is not present.");
+ assertDetailMessageCode(ex, null, new Object[] {ex.getVariableName()});
+
+ assertThat(ex.getHeaders().isEmpty()).isTrue();
+ }
+ @Test
+ void missingRequestCookieException() {
MissingRequestCookieException ex = new MissingRequestCookieException("oreo", this.methodParameter);
assertStatus(ex, HttpStatus.BAD_REQUEST);
@@ -223,7 +222,6 @@ class ErrorResponseExceptionTests {
@Test
void unsatisfiedServletRequestParameterException() {
-
UnsatisfiedServletRequestParameterException ex = new UnsatisfiedServletRequestParameterException(
new String[] { "foo=bar", "bar=baz" }, Collections.singletonMap("q", new String[] {"1"}));
@@ -236,7 +234,6 @@ class ErrorResponseExceptionTests {
@Test
void missingServletRequestPartException() {
-
MissingServletRequestPartException ex = new MissingServletRequestPartException("file");
assertStatus(ex, HttpStatus.BAD_REQUEST);
@@ -248,7 +245,6 @@ class ErrorResponseExceptionTests {
@Test
void methodArgumentNotValidException() {
-
ValidationTestHelper testHelper = new ValidationTestHelper(MethodArgumentNotValidException.class);
BindingResult result = testHelper.bindingResult();
@@ -280,7 +276,6 @@ class ErrorResponseExceptionTests {
@Test
void unsupportedMediaTypeStatusException() {
-
List mediaTypes =
Arrays.asList(MediaType.APPLICATION_JSON, MediaType.APPLICATION_CBOR);
@@ -298,7 +293,6 @@ class ErrorResponseExceptionTests {
@Test
void unsupportedMediaTypeStatusExceptionWithParseError() {
-
ErrorResponse ex = new UnsupportedMediaTypeStatusException(
"Could not parse Accept header: Invalid mime type \"foo\": does not contain '/'");
@@ -311,7 +305,6 @@ class ErrorResponseExceptionTests {
@Test
void notAcceptableStatusException() {
-
List mediaTypes = Arrays.asList(MediaType.APPLICATION_JSON, MediaType.APPLICATION_CBOR);
NotAcceptableStatusException ex = new NotAcceptableStatusException(mediaTypes);
@@ -325,7 +318,6 @@ class ErrorResponseExceptionTests {
@Test
void notAcceptableStatusExceptionWithParseError() {
-
ErrorResponse ex = new NotAcceptableStatusException(
"Could not parse Accept header: Invalid mime type \"foo\": does not contain '/'");
@@ -338,7 +330,6 @@ class ErrorResponseExceptionTests {
@Test
void serverErrorException() {
-
ServerErrorException ex = new ServerErrorException("Failure", null);
assertStatus(ex, HttpStatus.INTERNAL_SERVER_ERROR);
@@ -350,7 +341,6 @@ class ErrorResponseExceptionTests {
@Test
void missingRequestValueException() {
-
MissingRequestValueException ex =
new MissingRequestValueException("foo", String.class, "header", this.methodParameter);
@@ -363,7 +353,6 @@ class ErrorResponseExceptionTests {
@Test
void unsatisfiedRequestParameterException() {
-
UnsatisfiedRequestParameterException ex =
new UnsatisfiedRequestParameterException(
Arrays.asList("foo=bar", "bar=baz"),
@@ -378,7 +367,6 @@ class ErrorResponseExceptionTests {
@Test
void webExchangeBindException() {
-
ValidationTestHelper testHelper = new ValidationTestHelper(WebExchangeBindException.class);
BindingResult result = testHelper.bindingResult();
@@ -393,7 +381,6 @@ class ErrorResponseExceptionTests {
@Test
void methodNotAllowedException() {
-
List supportedMethods = Arrays.asList(HttpMethod.GET, HttpMethod.POST);
MethodNotAllowedException ex = new MethodNotAllowedException(HttpMethod.PUT, supportedMethods);
@@ -407,7 +394,6 @@ class ErrorResponseExceptionTests {
@Test
void methodNotAllowedExceptionWithoutSupportedMethods() {
-
MethodNotAllowedException ex = new MethodNotAllowedException(HttpMethod.PUT, Collections.emptyList());
assertStatus(ex, HttpStatus.METHOD_NOT_ALLOWED);
@@ -417,9 +403,8 @@ class ErrorResponseExceptionTests {
assertThat(ex.getHeaders()).isEmpty();
}
- @Test // gh-30300
+ @Test // gh-30300
void responseStatusException() {
-
Locale locale = Locale.UK;
LocaleContextHolder.setLocale(locale);
@@ -519,7 +504,6 @@ class ErrorResponseExceptionTests {
assertThat(BindErrorUtils.resolve(errors, this.messageSource, Locale.UK)).hasSize(4)
.containsValues("Bean A message", "Bean B message", "name is required", "age is below minimum");
}
-
}
}
diff --git a/src/checkstyle/checkstyle-suppressions.xml b/src/checkstyle/checkstyle-suppressions.xml
index c54f1501ffe..129dad4d9bb 100644
--- a/src/checkstyle/checkstyle-suppressions.xml
+++ b/src/checkstyle/checkstyle-suppressions.xml
@@ -128,6 +128,7 @@
+