From c643c4f14c2c4050a59ee1cc9cd61a800eab4302 Mon Sep 17 00:00:00 2001 From: USJUN Date: Sat, 11 Jul 2026 09:32:35 +0900 Subject: [PATCH] Write ResponseStatusException headers in WebFlux error responses See gh-18831 Signed-off-by: USJUN --- .../error/AbstractErrorWebExceptionHandler.java | 11 +++++++++-- ...rrorWebExceptionHandlerIntegrationTests.java | 17 +++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/error/AbstractErrorWebExceptionHandler.java b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/error/AbstractErrorWebExceptionHandler.java index eec47aa35810..285fd879cff0 100644 --- a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/error/AbstractErrorWebExceptionHandler.java +++ b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/error/AbstractErrorWebExceptionHandler.java @@ -45,6 +45,7 @@ import org.springframework.web.reactive.function.server.ServerRequest; import org.springframework.web.reactive.function.server.ServerResponse; import org.springframework.web.reactive.result.view.ViewResolver; +import org.springframework.web.server.ResponseStatusException; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.util.DisconnectedClientHelper; import org.springframework.web.util.HtmlUtils; @@ -55,6 +56,7 @@ * @author Brian Clozel * @author Scott Frederick * @author Moritz Halbritter + * @author US JUN * @since 4.0.0 * @see ErrorAttributes */ @@ -296,7 +298,7 @@ public Mono handle(ServerWebExchange exchange, Throwable throwable) { .switchIfEmpty(Mono.error(throwable)) .flatMap((handler) -> handler.handle(request)) .doOnNext((response) -> logError(request, response, throwable)) - .flatMap((response) -> write(exchange, response)); + .flatMap((response) -> write(exchange, response, throwable)); } private boolean isDisconnectedClientError(Throwable ex) { @@ -333,7 +335,12 @@ private String formatRequest(ServerRequest request) { return "HTTP " + request.method() + " \"" + request.path() + query + "\""; } - private Mono write(ServerWebExchange exchange, ServerResponse response) { + private Mono write(ServerWebExchange exchange, ServerResponse response, Throwable throwable) { + if (throwable instanceof ResponseStatusException responseStatusException) { + responseStatusException.getHeaders() + .forEach((name, values) -> values + .forEach((value) -> exchange.getResponse().getHeaders().add(name, value))); + } // force content-type since writeTo won't overwrite response header values exchange.getResponse().getHeaders().setContentType(response.headers().getContentType()); return response.writeTo(exchange, new ResponseContext()); diff --git a/module/spring-boot-webflux/src/test/java/org/springframework/boot/webflux/autoconfigure/error/DefaultErrorWebExceptionHandlerIntegrationTests.java b/module/spring-boot-webflux/src/test/java/org/springframework/boot/webflux/autoconfigure/error/DefaultErrorWebExceptionHandlerIntegrationTests.java index 110a904261ca..93431eb5a27c 100644 --- a/module/spring-boot-webflux/src/test/java/org/springframework/boot/webflux/autoconfigure/error/DefaultErrorWebExceptionHandlerIntegrationTests.java +++ b/module/spring-boot-webflux/src/test/java/org/springframework/boot/webflux/autoconfigure/error/DefaultErrorWebExceptionHandlerIntegrationTests.java @@ -50,6 +50,8 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.codec.ServerCodecConfigurer; @@ -75,6 +77,7 @@ * * @author Brian Clozel * @author Scott Frederick + * @author US JUN */ @ExtendWith(OutputCaptureExtension.class) class DefaultErrorWebExceptionHandlerIntegrationTests { @@ -381,6 +384,20 @@ void statusException() { }); } + @Test // gh-18831 + void responseStatusExceptionHeaders() { + this.contextRunner.run((context) -> { + WebTestClient client = getWebClient(context); + client.post() + .uri("/badRequest") + .exchange() + .expectStatus() + .isEqualTo(HttpStatus.METHOD_NOT_ALLOWED) + .expectHeader() + .valueEquals(HttpHeaders.ALLOW, HttpMethod.GET.name()); + }); + } + @Test void defaultErrorView() { this.contextRunner