From 69df27a99ff191cbdfc9ecf7c19f3ac3e87888af Mon Sep 17 00:00:00 2001 From: Ingmar van Dijk Date: Mon, 6 Jul 2020 09:13:07 +0200 Subject: [PATCH 1/2] Throw 404 ResponseStatusException when no routes found This commit makes it possible to customize 404 responses generated by RouterFunctionWebHandler, by throwing an ResponseStatusException instead of returning a standard 404 response. See gh-25358 --- .../function/server/RouterFunctions.java | 12 +++------- .../function/server/RouterFunctionsTests.java | 24 +++++++++++++++++++ 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RouterFunctions.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RouterFunctions.java index 30157153d4ae..8b241f70cd9b 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RouterFunctions.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RouterFunctions.java @@ -32,10 +32,12 @@ import reactor.core.publisher.Mono; import org.springframework.core.io.Resource; +import org.springframework.http.HttpStatus; import org.springframework.http.codec.HttpMessageWriter; import org.springframework.http.server.reactive.HttpHandler; import org.springframework.util.Assert; import org.springframework.web.reactive.result.view.ViewResolver; +import org.springframework.web.server.ResponseStatusException; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.WebHandler; import org.springframework.web.server.adapter.WebHttpHandlerBuilder; @@ -1231,9 +1233,6 @@ public List viewResolvers() { private static class RouterFunctionWebHandler implements WebHandler { - private static final HandlerFunction NOT_FOUND_HANDLER = - request -> ServerResponse.notFound().build(); - private final HandlerStrategies strategies; private final RouterFunction routerFunction; @@ -1249,7 +1248,7 @@ public Mono handle(ServerWebExchange exchange) { ServerRequest request = new DefaultServerRequest(exchange, this.strategies.messageReaders()); addAttributes(exchange, request); return this.routerFunction.route(request) - .defaultIfEmpty(notFound()) + .switchIfEmpty(Mono.error(new ResponseStatusException(HttpStatus.NOT_FOUND))) .flatMap(handlerFunction -> wrapException(() -> handlerFunction.handle(request))) .flatMap(response -> wrapException(() -> response.writeTo(exchange, new HandlerStrategiesResponseContext(this.strategies)))); @@ -1261,11 +1260,6 @@ private void addAttributes(ServerWebExchange exchange, ServerRequest request) { attributes.put(REQUEST_ATTRIBUTE, request); } - @SuppressWarnings("unchecked") - private static HandlerFunction notFound() { - return (HandlerFunction) NOT_FOUND_HANDLER; - } - private static Mono wrapException(Supplier> supplier) { try { return supplier.get(); diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/RouterFunctionsTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/RouterFunctionsTests.java index 46b29bdf6cd4..ad6bfbdca9f7 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/RouterFunctionsTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/RouterFunctionsTests.java @@ -16,14 +16,17 @@ package org.springframework.web.reactive.function.server; +import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.jupiter.api.Test; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; +import org.springframework.core.io.buffer.DataBuffer; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseCookie; @@ -191,6 +194,27 @@ public void toHttpHandlerHandlerResponseStatusException() { assertThat(httpResponse.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); } + @Test + public void toHttpHandlerRouteNotFoundReturnsResponseStatusException() { + HandlerFunction handlerFunction = request -> ServerResponse.accepted().build(); + RouterFunction routerFunction = + RouterFunctions.route(RequestPredicates.GET("/path"), handlerFunction); + + HandlerStrategies handlerStrategies = HandlerStrategies.empty().exceptionHandler((exchange, ex) -> { + exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND); + DataBuffer buffer = exchange.getResponse().bufferFactory().wrap("Custom response".getBytes(StandardCharsets.UTF_8)); + return exchange.getResponse().writeWith(Flux.just(buffer)); + }).build(); + HttpHandler result = RouterFunctions.toHttpHandler(routerFunction, handlerStrategies); + assertThat(result).isNotNull(); + + MockServerHttpRequest httpRequest = MockServerHttpRequest.get("https://localhost").build(); + MockServerHttpResponse httpResponse = new MockServerHttpResponse(); + result.handle(httpRequest, httpResponse).block(); + assertThat(httpResponse.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); + assertThat(httpResponse.getBodyAsString().block()).isEqualTo("Custom response"); + } + @Test public void toHttpHandlerHandlerReturnResponseStatusExceptionInResponseWriteTo() { HandlerFunction handlerFunction = From b84fe99d07f9cb1261950121e8da7133bc645302 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Fri, 3 Dec 2021 12:00:23 +0100 Subject: [PATCH 2/2] Polish "Throw 404 ResponseStatusException when no routes found" See gh-25358 --- .../web/reactive/function/server/RouterFunctions.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RouterFunctions.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RouterFunctions.java index 8b241f70cd9b..739fc17a1c33 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RouterFunctions.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RouterFunctions.java @@ -1248,7 +1248,7 @@ public Mono handle(ServerWebExchange exchange) { ServerRequest request = new DefaultServerRequest(exchange, this.strategies.messageReaders()); addAttributes(exchange, request); return this.routerFunction.route(request) - .switchIfEmpty(Mono.error(new ResponseStatusException(HttpStatus.NOT_FOUND))) + .switchIfEmpty(createNotFoundError()) .flatMap(handlerFunction -> wrapException(() -> handlerFunction.handle(request))) .flatMap(response -> wrapException(() -> response.writeTo(exchange, new HandlerStrategiesResponseContext(this.strategies)))); @@ -1260,6 +1260,11 @@ private void addAttributes(ServerWebExchange exchange, ServerRequest request) { attributes.put(REQUEST_ATTRIBUTE, request); } + private Mono createNotFoundError() { + return Mono.defer(() -> Mono.error(new ResponseStatusException(HttpStatus.NOT_FOUND, + "No matching router function"))); + } + private static Mono wrapException(Supplier> supplier) { try { return supplier.get();