Add resource redirection to WebMVC functional router
See gh-27257
This commit is contained in:
parent
2d7b2e59b6
commit
d3ca6f9f6a
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2024 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.web.servlet.function;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup function used by {@link RouterFunctions#resource(RequestPredicate, Resource)} and
|
||||||
|
* {@link RouterFunctions#resource(RequestPredicate, Resource, java.util.function.BiConsumer)}.
|
||||||
|
*
|
||||||
|
* @author Sebastien Deleuze
|
||||||
|
* @since 6.1.4
|
||||||
|
*/
|
||||||
|
class PredicateResourceLookupFunction implements Function<ServerRequest, Optional<Resource>> {
|
||||||
|
|
||||||
|
private final RequestPredicate predicate;
|
||||||
|
|
||||||
|
private final Resource resource;
|
||||||
|
|
||||||
|
public PredicateResourceLookupFunction(RequestPredicate predicate, Resource resource) {
|
||||||
|
Assert.notNull(predicate, "'predicate' must not be null");
|
||||||
|
Assert.notNull(resource, "'resource' must not be null");
|
||||||
|
this.predicate = predicate;
|
||||||
|
this.resource = resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Resource> apply(ServerRequest serverRequest) {
|
||||||
|
return this.predicate.test(serverRequest) ? Optional.of(this.resource) : Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2023 the original author or authors.
|
* Copyright 2002-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -37,6 +37,7 @@ import org.springframework.util.Assert;
|
||||||
* Default implementation of {@link RouterFunctions.Builder}.
|
* Default implementation of {@link RouterFunctions.Builder}.
|
||||||
*
|
*
|
||||||
* @author Arjen Poutsma
|
* @author Arjen Poutsma
|
||||||
|
* @author Sebastien Deleuze
|
||||||
* @since 5.2
|
* @since 5.2
|
||||||
*/
|
*/
|
||||||
class RouterFunctionBuilder implements RouterFunctions.Builder {
|
class RouterFunctionBuilder implements RouterFunctions.Builder {
|
||||||
|
@ -236,6 +237,17 @@ class RouterFunctionBuilder implements RouterFunctions.Builder {
|
||||||
return add(RouterFunctions.route(predicate, handlerFunction));
|
return add(RouterFunctions.route(predicate, handlerFunction));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RouterFunctions.Builder resource(RequestPredicate predicate, Resource resource) {
|
||||||
|
return add(RouterFunctions.resource(predicate, resource));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RouterFunctions.Builder resource(RequestPredicate predicate, Resource resource,
|
||||||
|
BiConsumer<Resource, HttpHeaders> headersConsumer) {
|
||||||
|
return add(RouterFunctions.resource(predicate, resource, headersConsumer));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RouterFunctions.Builder resources(String pattern, Resource location) {
|
public RouterFunctions.Builder resources(String pattern, Resource location) {
|
||||||
return add(RouterFunctions.resources(pattern, location));
|
return add(RouterFunctions.resources(pattern, location));
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2023 the original author or authors.
|
* Copyright 2002-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -45,6 +45,7 @@ import org.springframework.web.util.pattern.PathPatternParser;
|
||||||
* function.
|
* function.
|
||||||
*
|
*
|
||||||
* @author Arjen Poutsma
|
* @author Arjen Poutsma
|
||||||
|
* @author Sebastien Deleuze
|
||||||
* @since 5.2
|
* @since 5.2
|
||||||
*/
|
*/
|
||||||
public abstract class RouterFunctions {
|
public abstract class RouterFunctions {
|
||||||
|
@ -128,6 +129,40 @@ public abstract class RouterFunctions {
|
||||||
return new DefaultNestedRouterFunction<>(predicate, routerFunction);
|
return new DefaultNestedRouterFunction<>(predicate, routerFunction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route requests that match the given predicate to the given resource.
|
||||||
|
* For instance
|
||||||
|
* <pre class="code">
|
||||||
|
* Resource resource = new ClassPathResource("static/index.html")
|
||||||
|
* RouterFunction<ServerResponse> resources = RouterFunctions.resource(path("/api/**").negate(), resource);
|
||||||
|
* </pre>
|
||||||
|
* @param predicate predicate to match
|
||||||
|
* @param resource the resources to serve
|
||||||
|
* @return a router function that routes to a resource
|
||||||
|
* @since 6.1.4
|
||||||
|
*/
|
||||||
|
public static RouterFunction<ServerResponse> resource(RequestPredicate predicate, Resource resource) {
|
||||||
|
return resources(new PredicateResourceLookupFunction(predicate, resource), (consumerResource, httpHeaders) -> {});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route requests that match the given predicate to the given resource.
|
||||||
|
* For instance
|
||||||
|
* <pre class="code">
|
||||||
|
* Resource resource = new ClassPathResource("static/index.html")
|
||||||
|
* RouterFunction<ServerResponse> resources = RouterFunctions.resource(path("/api/**").negate(), resource);
|
||||||
|
* </pre>
|
||||||
|
* @param predicate predicate to match
|
||||||
|
* @param resource the resources to serve
|
||||||
|
* @param headersConsumer provides access to the HTTP headers for served resources
|
||||||
|
* @return a router function that routes to a resource
|
||||||
|
* @since 6.1.4
|
||||||
|
*/
|
||||||
|
public static RouterFunction<ServerResponse> resource(RequestPredicate predicate, Resource resource,
|
||||||
|
BiConsumer<Resource, HttpHeaders> headersConsumer) {
|
||||||
|
return resources(new PredicateResourceLookupFunction(predicate, resource), headersConsumer);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Route requests that match the given pattern to resources relative to the given root location.
|
* Route requests that match the given pattern to resources relative to the given root location.
|
||||||
* For instance
|
* For instance
|
||||||
|
@ -602,6 +637,35 @@ public abstract class RouterFunctions {
|
||||||
*/
|
*/
|
||||||
Builder add(RouterFunction<ServerResponse> routerFunction);
|
Builder add(RouterFunction<ServerResponse> routerFunction);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route requests that match the given predicate to the given resource.
|
||||||
|
* For instance
|
||||||
|
* <pre class="code">
|
||||||
|
* Resource resource = new ClassPathResource("static/index.html")
|
||||||
|
* RouterFunction<ServerResponse> resources = RouterFunctions.resource(path("/api/**").negate(), resource);
|
||||||
|
* </pre>
|
||||||
|
* @param predicate predicate to match
|
||||||
|
* @param resource the resources to serve
|
||||||
|
* @return a router function that routes to a resource
|
||||||
|
* @since 6.1.4
|
||||||
|
*/
|
||||||
|
Builder resource(RequestPredicate predicate, Resource resource);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route requests that match the given predicate to the given resource.
|
||||||
|
* For instance
|
||||||
|
* <pre class="code">
|
||||||
|
* Resource resource = new ClassPathResource("static/index.html")
|
||||||
|
* RouterFunction<ServerResponse> resources = RouterFunctions.resource(path("/api/**").negate(), resource);
|
||||||
|
* </pre>
|
||||||
|
* @param predicate predicate to match
|
||||||
|
* @param resource the resources to serve
|
||||||
|
* @param headersConsumer provides access to the HTTP headers for served resources
|
||||||
|
* @return a router function that routes to a resource
|
||||||
|
* @since 6.1.4
|
||||||
|
*/
|
||||||
|
Builder resource(RequestPredicate predicate, Resource resource, BiConsumer<Resource, HttpHeaders> headersConsumer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Route requests that match the given pattern to resources relative to the given root location.
|
* Route requests that match the given pattern to resources relative to the given root location.
|
||||||
* For instance
|
* For instance
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2023 the original author or authors.
|
* Copyright 2002-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -612,6 +612,15 @@ class RouterFunctionDsl internal constructor (private val init: (RouterFunctionD
|
||||||
builder.add(RouterFunctions.route(RequestPredicates.path(this), HandlerFunction(f)))
|
builder.add(RouterFunctions.route(RequestPredicates.path(this), HandlerFunction(f)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route requests that match the given predicate to the given resource.
|
||||||
|
* @see RouterFunctions.resource
|
||||||
|
* @since 6.1.4
|
||||||
|
*/
|
||||||
|
fun resource(predicate: RequestPredicate, resource: Resource) {
|
||||||
|
builder.resource(predicate, resource)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Route requests that match the given pattern to resources relative to the given root location.
|
* Route requests that match the given pattern to resources relative to the given root location.
|
||||||
* @see RouterFunctions.resources
|
* @see RouterFunctions.resources
|
||||||
|
|
|
@ -39,11 +39,13 @@ import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
|
||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.springframework.web.servlet.function.RequestPredicates.HEAD;
|
import static org.springframework.web.servlet.function.RequestPredicates.HEAD;
|
||||||
|
import static org.springframework.web.servlet.function.RequestPredicates.path;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link RouterFunctionBuilder}.
|
* Tests for {@link RouterFunctionBuilder}.
|
||||||
*
|
*
|
||||||
* @author Arjen Poutsma
|
* @author Arjen Poutsma
|
||||||
|
* @author Sebastien Deleuze
|
||||||
*/
|
*/
|
||||||
class RouterFunctionBuilderTests {
|
class RouterFunctionBuilderTests {
|
||||||
|
|
||||||
|
@ -96,6 +98,23 @@ class RouterFunctionBuilderTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void resource() {
|
||||||
|
Resource resource = new ClassPathResource("/org/springframework/web/servlet/function/response.txt");
|
||||||
|
assertThat(resource.exists()).isTrue();
|
||||||
|
|
||||||
|
RouterFunction<ServerResponse> route = RouterFunctions.route()
|
||||||
|
.resource(path("/test"), resource)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
ServerRequest resourceRequest = initRequest("GET", "/test");
|
||||||
|
|
||||||
|
Optional<HttpStatusCode> responseStatus = route.route(resourceRequest)
|
||||||
|
.map(handlerFunction -> handle(handlerFunction, resourceRequest))
|
||||||
|
.map(ServerResponse::statusCode);
|
||||||
|
assertThat(responseStatus).contains(HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void resources() {
|
void resources() {
|
||||||
Resource resource = new ClassPathResource("/org/springframework/web/servlet/function/");
|
Resource resource = new ClassPathResource("/org/springframework/web/servlet/function/");
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2022 the original author or authors.
|
* Copyright 2002-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -74,14 +74,6 @@ class RouterFunctionDslTests {
|
||||||
assertThat(sampleRouter().route(request).isPresent).isTrue()
|
assertThat(sampleRouter().route(request).isPresent).isTrue()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun resourceByPath() {
|
|
||||||
val servletRequest = PathPatternsTestUtils.initRequest(
|
|
||||||
"GET", "/org/springframework/web/servlet/function/response.txt", true)
|
|
||||||
val request = DefaultServerRequest(servletRequest, emptyList())
|
|
||||||
assertThat(sampleRouter().route(request).isPresent).isTrue()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun method() {
|
fun method() {
|
||||||
val servletRequest = PathPatternsTestUtils.initRequest("PATCH", "/", true)
|
val servletRequest = PathPatternsTestUtils.initRequest("PATCH", "/", true)
|
||||||
|
@ -98,6 +90,20 @@ class RouterFunctionDslTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun resource() {
|
fun resource() {
|
||||||
|
val servletRequest = PathPatternsTestUtils.initRequest("GET","/response2.txt", true)
|
||||||
|
val request = DefaultServerRequest(servletRequest, emptyList())
|
||||||
|
assertThat(sampleRouter().route(request).isPresent).isTrue()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun resources() {
|
||||||
|
val servletRequest = PathPatternsTestUtils.initRequest("GET", "/resources/response.txt", true)
|
||||||
|
val request = DefaultServerRequest(servletRequest, emptyList())
|
||||||
|
assertThat(sampleRouter().route(request).isPresent).isTrue()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun resourcesLookupFunction() {
|
||||||
val servletRequest = PathPatternsTestUtils.initRequest("GET", "/response.txt", true)
|
val servletRequest = PathPatternsTestUtils.initRequest("GET", "/response.txt", true)
|
||||||
val request = DefaultServerRequest(servletRequest, emptyList())
|
val request = DefaultServerRequest(servletRequest, emptyList())
|
||||||
assertThat(sampleRouter().route(request).isPresent).isTrue()
|
assertThat(sampleRouter().route(request).isPresent).isTrue()
|
||||||
|
@ -168,8 +174,9 @@ class RouterFunctionDslTests {
|
||||||
GET("/api/foo/", ::handle)
|
GET("/api/foo/", ::handle)
|
||||||
}
|
}
|
||||||
headers({ it.header("bar").isNotEmpty() }, ::handle)
|
headers({ it.header("bar").isNotEmpty() }, ::handle)
|
||||||
resources("/org/springframework/web/servlet/function/**",
|
resource(path("/response2.txt"), ClassPathResource("/org/springframework/web/servlet/function/response.txt"))
|
||||||
ClassPathResource("/org/springframework/web/servlet/function/response.txt"))
|
resources("/resources/**",
|
||||||
|
ClassPathResource("/org/springframework/web/servlet/function/"))
|
||||||
resources {
|
resources {
|
||||||
if (it.path() == "/response.txt") {
|
if (it.path() == "/response.txt") {
|
||||||
ClassPathResource("/org/springframework/web/servlet/function/response.txt")
|
ClassPathResource("/org/springframework/web/servlet/function/response.txt")
|
||||||
|
|
Loading…
Reference in New Issue