diff --git a/spring-web/src/main/java/org/springframework/web/ErrorResponse.java b/spring-web/src/main/java/org/springframework/web/ErrorResponse.java index dfc545bb2da..c83bcdb1c4b 100644 --- a/spring-web/src/main/java/org/springframework/web/ErrorResponse.java +++ b/spring-web/src/main/java/org/springframework/web/ErrorResponse.java @@ -40,7 +40,6 @@ import org.springframework.lang.Nullable; * {@code @RestController} or {@code RestControllerAdvice} class. * * @author Rossen Stoyanchev - * @author Yanming Zhou * @since 6.0 * @see ErrorResponseException */ @@ -143,14 +142,6 @@ public interface ErrorResponse { if (detail != null) { getBody().setDetail(detail); } - else { - // detail from ResponseStatusException reason may be message code - detail = getBody().getDetail(); - if (detail != null) { - detail = messageSource.getMessage(detail, null, detail, locale); - getBody().setDetail(detail); - } - } String title = messageSource.getMessage(getTitleMessageCode(), null, null, locale); if (title != null) { getBody().setTitle(title); diff --git a/spring-web/src/main/java/org/springframework/web/server/ResponseStatusException.java b/spring-web/src/main/java/org/springframework/web/server/ResponseStatusException.java index c779133e5e6..8e9c039aae7 100644 --- a/spring-web/src/main/java/org/springframework/web/server/ResponseStatusException.java +++ b/spring-web/src/main/java/org/springframework/web/server/ResponseStatusException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 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. @@ -16,6 +16,9 @@ package org.springframework.web.server; +import java.util.Locale; + +import org.springframework.context.MessageSource; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatusCode; import org.springframework.http.ProblemDetail; @@ -127,6 +130,23 @@ public class ResponseStatusException extends ErrorResponseException { return HttpHeaders.EMPTY; } + @Override + public ProblemDetail updateAndGetBody(@Nullable MessageSource messageSource, Locale locale) { + super.updateAndGetBody(messageSource, locale); + + // The reason may be a code (consistent with ResponseStatusExceptionResolver) + + if (messageSource != null && getReason() != null && getReason().equals(getBody().getDetail())) { + Object[] arguments = getDetailMessageArguments(messageSource, locale); + String resolved = messageSource.getMessage(getReason(), arguments, null, locale); + if (resolved != null) { + getBody().setDetail(resolved); + } + } + + return getBody(); + } + @Override public String getMessage() { return getStatusCode() + (this.reason != null ? " \"" + this.reason + "\"" : ""); 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 1427aa964b0..6405a092cd1 100644 --- a/spring-web/src/test/java/org/springframework/web/ErrorResponseExceptionTests.java +++ b/spring-web/src/test/java/org/springframework/web/ErrorResponseExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -25,6 +25,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.context.MessageSourceResolvable; +import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.context.support.StaticMessageSource; import org.springframework.core.MethodParameter; import org.springframework.http.HttpHeaders; @@ -51,6 +52,7 @@ import org.springframework.web.multipart.support.MissingServletRequestPartExcept import org.springframework.web.server.MethodNotAllowedException; import org.springframework.web.server.MissingRequestValueException; import org.springframework.web.server.NotAcceptableStatusException; +import org.springframework.web.server.ResponseStatusException; import org.springframework.web.server.ServerErrorException; import org.springframework.web.server.UnsatisfiedRequestParameterException; import org.springframework.web.server.UnsupportedMediaTypeStatusException; @@ -415,6 +417,28 @@ public class ErrorResponseExceptionTests { assertThat(ex.getHeaders()).isEmpty(); } + @Test // gh-30300 + void responseStatusException() { + + Locale locale = Locale.UK; + LocaleContextHolder.setLocale(locale); + + try { + String reason = "bad.request"; + String message = "Breaking Bad Request"; + StaticMessageSource messageSource = new StaticMessageSource(); + messageSource.addMessage(reason, locale, message); + + ResponseStatusException ex = new ResponseStatusException(HttpStatus.BAD_REQUEST, reason); + + ProblemDetail problemDetail = ex.updateAndGetBody(messageSource, locale); + assertThat(problemDetail.getDetail()).isEqualTo(message); + } + finally { + LocaleContextHolder.resetLocaleContext(); + } + } + private void assertStatus(ErrorResponse ex, HttpStatus status) { ProblemDetail body = ex.getBody(); assertThat(ex.getStatusCode()).isEqualTo(status); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandlerTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandlerTests.java index bee3c569e40..94dcc038e72 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandlerTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -201,21 +201,21 @@ public class ResponseEntityExceptionHandlerTests { } } - @Test + @Test // gh-30300 public void reasonAsDetailShouldBeUpdatedViaMessageSource() { Locale locale = Locale.UK; LocaleContextHolder.setLocale(locale); - String code = "bad.request"; + String reason = "bad.request"; String message = "Breaking Bad Request"; try { StaticMessageSource messageSource = new StaticMessageSource(); - messageSource.addMessage(code, locale, message); + messageSource.addMessage(reason, locale, message); this.exceptionHandler.setMessageSource(messageSource); - ResponseEntity entity = testException(new ResponseStatusException(HttpStatus.BAD_REQUEST, code)); + ResponseEntity entity = testException(new ResponseStatusException(HttpStatus.BAD_REQUEST, reason)); ProblemDetail body = (ProblemDetail) entity.getBody(); assertThat(body.getDetail()).isEqualTo(message);