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 049d03ce27..27302055a9 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 @@ -123,14 +123,35 @@ public abstract class RouterFunctions { * For instance *
 	 * Resource location = new FileSystemResource("public-resources/");
-	 * RoutingFunction<ServerResponse> resources = RouterFunctions.resources("/resources/**", location);
+	 * RouterFunction<ServerResponse> resources = RouterFunctions.resources("/resources/**", location);
      * 
* @param pattern the pattern to match * @param location the location directory relative to which resources should be resolved * @return a router function that routes to resources + * @see #resourceLookupFunction(String, Resource) */ public static RouterFunction resources(String pattern, Resource location) { - return resources(new PathResourceLookupFunction(pattern, location)); + return resources(resourceLookupFunction(pattern, location)); + } + + /** + * Returns the resource lookup function used by {@link #resources(String, Resource)}. + * The returned function can be {@linkplain Function#andThen(Function) composed} on, for + * instance to return a default resource when the lookup function does not match: + *
+	 * Mono<Resource> defaultResource = Mono.just(new ClassPathResource("index.html"));
+	 * Function<ServerRequest, Mono<Resource>> lookupFunction =
+	 *   RouterFunctions.resourceLookupFunction("/resources/**", new FileSystemResource("public-resources/"))
+	 *     .andThen(resourceMono -> resourceMono.switchIfEmpty(defaultResource));
+	 *
+	 * RouterFunction<ServerResponse> resources = RouterFunctions.resources(lookupFunction);
+     * 
+ * @param pattern the pattern to match + * @param location the location directory relative to which resources should be resolved + * @return the default resource lookup function for the given parameters. + */ + public static Function> resourceLookupFunction(String pattern, Resource location) { + return new PathResourceLookupFunction(pattern, location); } /** diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/PathResourceLookupFunctionTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/PathResourceLookupFunctionTests.java index b734ffaa37..ee5342bcc2 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/PathResourceLookupFunctionTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/PathResourceLookupFunctionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 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. @@ -19,6 +19,7 @@ package org.springframework.web.reactive.function.server; import java.io.File; import java.io.IOException; import java.net.URI; +import java.util.function.Function; import org.junit.Test; import reactor.core.publisher.Mono; @@ -95,4 +96,37 @@ public class PathResourceLookupFunctionTests { .verify(); } + @Test + public void composeResourceLookupFunction() throws Exception { + + Function> lookupFunction = + new PathResourceLookupFunction("/resources/**", + new ClassPathResource("org/springframework/web/reactive/function/server/")); + + ClassPathResource defaultResource = new ClassPathResource("response.txt", getClass()); + + Function> customLookupFunction = + lookupFunction.andThen(resourceMono -> resourceMono + .switchIfEmpty(Mono.just(defaultResource))); + + MockServerRequest request = MockServerRequest.builder() + .uri(new URI("http://localhost/resources/foo")) + .build(); + + Mono result = customLookupFunction.apply(request); + StepVerifier.create(result) + .expectNextMatches(resource -> { + try { + return defaultResource.getFile().equals(resource.getFile()); + } + catch (IOException ex) { + return false; + } + }) + .expectComplete() + .verify(); + + } + + } \ No newline at end of file