ResponseStatusException should not override custom reason

Prior to this commit, `ResponseStatusException` would resolve the
"detail" part of a problem detail response from message codes only
(default or custom ones). If the `reason` given as an argument to the
exception was a custom message, it would be overwritten in the process.

This commit ensures that we use custom reason messages when they don't
resolve as message codes.

Fixes gh-36984
This commit is contained in:
Brian Clozel
2026-09-22 16:56:40 +02:00
parent edd497c20f
commit 7be3a61a14
2 changed files with 38 additions and 11 deletions
@@ -17,6 +17,7 @@
package org.springframework.web.server;
import java.util.Locale;
import java.util.Objects;
import org.jspecify.annotations.Nullable;
@@ -117,19 +118,23 @@ public class ResponseStatusException extends ErrorResponseException {
@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);
String reason = getReason();
if (reason != null) {
boolean detailNotCustomized = reason.equals(getBody().getDetail());
super.updateAndGetBody(messageSource, locale);
if (!detailNotCustomized) {
return getBody();
}
// The reason may itself be a code (consistent with ResponseStatusExceptionResolver)
String resolved = null;
if (messageSource != null) {
resolved = messageSource.getMessage(reason, getDetailMessageArguments(messageSource, locale), null, locale);
}
// set to the custom reason if no message was resolved
getBody().setDetail(Objects.requireNonNullElse(resolved, reason));
return getBody();
}
return getBody();
return super.updateAndGetBody(messageSource, locale);
}
@Override
@@ -424,6 +424,28 @@ class ErrorResponseExceptionTests {
}
}
@Test // gh-36984
void responseStatusExceptionCustomReason() {
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);
String customReason = "my custom reason";
ResponseStatusException ex = new ResponseStatusException(HttpStatus.BAD_REQUEST, customReason);
ProblemDetail problemDetail = ex.updateAndGetBody(messageSource, locale);
assertThat(problemDetail.getDetail()).isEqualTo(customReason);
}
finally {
LocaleContextHolder.resetLocaleContext();
}
}
private void assertStatus(ErrorResponse ex, HttpStatus status) {
ProblemDetail body = ex.getBody();
assertThat(ex.getStatusCode()).isEqualTo(status);