diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/error/AbstractErrorWebExceptionHandler.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/error/AbstractErrorWebExceptionHandler.java index 5c61a3854d..18aea21c8e 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/error/AbstractErrorWebExceptionHandler.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/error/AbstractErrorWebExceptionHandler.java @@ -220,12 +220,17 @@ public abstract class AbstractErrorWebExceptionHandler @Override public Mono handle(ServerWebExchange exchange, Throwable throwable) { - this.errorAttributes.storeErrorInformation(throwable, exchange); - ServerRequest request = ServerRequest.create(exchange, this.messageReaders); - return getRoutingFunction(this.errorAttributes).route(request) - .switchIfEmpty(Mono.error(throwable)) - .flatMap((handler) -> handler.handle(request)) - .flatMap((response) -> write(exchange, response)); + if (!exchange.getResponse().isCommitted()) { + this.errorAttributes.storeErrorInformation(throwable, exchange); + ServerRequest request = ServerRequest.create(exchange, this.messageReaders); + return getRoutingFunction(this.errorAttributes).route(request) + .switchIfEmpty(Mono.error(throwable)) + .flatMap((handler) -> handler.handle(request)) + .flatMap((response) -> write(exchange, response)); + } + else { + return Mono.error(throwable); + } } private Mono write(ServerWebExchange exchange, diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/error/DefaultErrorWebExceptionHandlerIntegrationTest.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/error/DefaultErrorWebExceptionHandlerIntegrationTest.java index 37eb53f346..c319c7d8a1 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/error/DefaultErrorWebExceptionHandlerIntegrationTest.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/error/DefaultErrorWebExceptionHandlerIntegrationTest.java @@ -52,6 +52,7 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.server.ResponseStatusException; +import org.springframework.web.server.ServerWebExchange; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.allOf; @@ -188,6 +189,16 @@ public class DefaultErrorWebExceptionHandlerIntegrationTest { containsString("IllegalStateException"))); } + @Test + public void responseCommitted() throws Exception { + load(); + this.webTestClient.get().uri("/commit").exchange() + .expectStatus().isEqualTo(HttpStatus.OK) + .expectBody().isEmpty(); + this.output.expect(not(containsString("java.lang.UnsupportedOperationException"))); + this.output.expect(containsString("java.lang.IllegalStateException: already committed!")); + } + private void load(String... arguments) { List args = new ArrayList<>(); args.add("--server.port=0"); @@ -235,6 +246,13 @@ public class DefaultErrorWebExceptionHandlerIntegrationTest { return Mono.error(new ResponseStatusException(HttpStatus.BAD_REQUEST)); } + @GetMapping("/commit") + public Mono commit(ServerWebExchange exchange) { + return exchange + .getResponse().writeWith(Mono.empty()) + .then(Mono.error(new IllegalStateException("already committed!"))); + } + @PostMapping(path = "/bind", produces = "application/json") @ResponseBody public String bodyValidation(@Valid @RequestBody DummyBody body) {