Renamed RouterFunction composition methods

Renamed `RouterFunction.andSame()` to `and()`, and `and()` to
`andOther()`. The reason for this change is that we can expect most
RouterFunctions to be parameterized over ServerResponse, and thus it
makes sense to use the shortest composition method (`and()`) for
composing router functions of the same type.

When a user composes different response types, such as composing a
`RouterFunction<RenderingResponse>` with an
`RouterFunction<EntityResponse<?>`, the `andOther` method is to be used,
but this is a less common scenario.
This commit is contained in:
Arjen Poutsma 2017-02-14 15:42:01 +01:00
parent 276f896956
commit bb9d3e1680
3 changed files with 42 additions and 33 deletions

View File

@ -16,8 +16,6 @@
package org.springframework.web.reactive.function.server;
import java.util.Optional;
import reactor.core.publisher.Mono;
/**
@ -33,7 +31,7 @@ public interface RouterFunction<T extends ServerResponse> {
/**
* Return the {@linkplain HandlerFunction handler function} that matches the given request.
* @param request the request to route to
* @param request the request to route
* @return an {@code Mono} describing the {@code HandlerFunction} that matches this request,
* or an empty {@code Mono} if there is no match
*/
@ -41,25 +39,27 @@ public interface RouterFunction<T extends ServerResponse> {
/**
* Return a composed routing function that first invokes this function,
* and then invokes the {@code other} function (of the same type {@code T})
* and then invokes the {@code other} function (of the same response type {@code T})
* if this route had {@linkplain Mono#empty() no result}.
* @param other the function of type {@code T} to apply when this function has no result
* @return a composed function that first routes with this function and then the
* {@code other} function if this function has no result
* @see #andOther(RouterFunction)
*/
default RouterFunction<T> andSame(RouterFunction<T> other) {
default RouterFunction<T> and(RouterFunction<T> other) {
return request -> this.route(request).otherwiseIfEmpty(other.route(request));
}
/**
* Return a composed routing function that first invokes this function,
* and then invokes the {@code other} function (of a different type) if this route had
* {@linkplain Optional#empty() no result}.
* and then invokes the {@code other} function (of a different response type) if this route had
* {@linkplain Mono#empty() no result}.
* @param other the function to apply when this function has no result
* @return a composed function that first routes with this function and then the
* {@code other} function if this function has no result
* @see #and(RouterFunction)
*/
default RouterFunction<?> and(RouterFunction<?> other) {
default RouterFunction<?> andOther(RouterFunction<?> other) {
return request -> this.route(request)
.map(RouterFunctions::cast)
.otherwiseIfEmpty(other.route(request).map(RouterFunctions::cast));
@ -70,16 +70,13 @@ public interface RouterFunction<T extends ServerResponse> {
* and then routes to the given handler function if the given request predicate applies. This
* method is a convenient combination of {@link #and(RouterFunction)} and
* {@link RouterFunctions#route(RequestPredicate, HandlerFunction)}.
* @param <S> the handler function type
* @param predicate the predicate to test
* @param handlerFunction the handler function to route to
* @return a composed function that first routes with this function and then the function
* created from {@code predicate} and {@code handlerFunction} if this
* function has no result
*/
default <S extends ServerResponse> RouterFunction<?> andRoute(RequestPredicate predicate,
HandlerFunction<S> handlerFunction) {
default RouterFunction<T> andRoute(RequestPredicate predicate, HandlerFunction<T> handlerFunction) {
return and(RouterFunctions.route(predicate, handlerFunction));
}

View File

@ -1,3 +1,19 @@
/*
* Copyright 2002-2017 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
*
* http://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.reactive.function.server
import org.springframework.core.io.Resource
@ -137,9 +153,8 @@ class RouterDsl {
routes += RouterFunctions.resources(lookupFunction)
}
@Suppress("UNCHECKED_CAST")
fun router(): RouterFunction<ServerResponse> {
return routes().reduce(RouterFunction<*>::and) as RouterFunction<ServerResponse>
return routes().reduce(RouterFunction<ServerResponse>::and)
}
operator fun invoke(request: ServerRequest): Mono<HandlerFunction<ServerResponse>> {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@ -30,12 +30,12 @@ import static org.springframework.web.reactive.function.BodyInserters.fromObject
public class RouterFunctionTests {
@Test
public void andSame() throws Exception {
public void and() throws Exception {
HandlerFunction<ServerResponse> handlerFunction = request -> ServerResponse.ok().build();
RouterFunction<ServerResponse> routerFunction1 = request -> Mono.empty();
RouterFunction<ServerResponse> routerFunction2 = request -> Mono.just(handlerFunction);
RouterFunction<ServerResponse> result = routerFunction1.andSame(routerFunction2);
RouterFunction<ServerResponse> result = routerFunction1.and(routerFunction2);
assertNotNull(result);
MockServerRequest request = MockServerRequest.builder().build();
@ -48,14 +48,14 @@ public class RouterFunctionTests {
}
@Test
public void and() throws Exception {
public void andOther() throws Exception {
HandlerFunction<ServerResponse> handlerFunction =
request -> ServerResponse.ok().body(fromObject("42"));
RouterFunction<?> routerFunction1 = request -> Mono.empty();
RouterFunction<ServerResponse> routerFunction2 =
request -> Mono.just(handlerFunction);
RouterFunction<?> result = routerFunction1.and(routerFunction2);
RouterFunction<?> result = routerFunction1.andOther(routerFunction2);
assertNotNull(result);
MockServerRequest request = MockServerRequest.builder().build();
@ -69,10 +69,10 @@ public class RouterFunctionTests {
@Test
public void andRoute() throws Exception {
RouterFunction<?> routerFunction1 = request -> Mono.empty();
RouterFunction<ServerResponse> routerFunction1 = request -> Mono.empty();
RequestPredicate requestPredicate = request -> true;
RouterFunction<?> result = routerFunction1.andRoute(requestPredicate, this::handlerMethod);
RouterFunction<ServerResponse> result = routerFunction1.andRoute(requestPredicate, this::handlerMethod);
assertNotNull(result);
MockServerRequest request = MockServerRequest.builder().build();
@ -88,32 +88,30 @@ public class RouterFunctionTests {
return ServerResponse.ok().body(fromObject("42"));
}
/*
TODO: enable when ServerEntityResponse is reintroduced
@Test
public void filter() throws Exception {
HandlerFunction<ServerResponse> handlerFunction = request -> ServerResponse.ok().body(fromObject("42"));
RouterFunction<ServerResponse> routerFunction = request -> Mono.just(handlerFunction);
Mono<String> stringMono = Mono.just("42");
HandlerFunction<EntityResponse<Mono<String>>> handlerFunction = request -> EntityResponse.fromPublisher(stringMono, String.class).build();
RouterFunction<EntityResponse<Mono<String>>> routerFunction = request -> Mono.just(handlerFunction);
HandlerFilterFunction<String, Integer> filterFunction =
HandlerFilterFunction<EntityResponse<Mono<String>>, EntityResponse<Mono<Integer>>> filterFunction =
(request, next) -> next.handle(request).then(
response -> {
Flux<Integer> body = Flux.from(response.body())
Mono<Integer> intMono = response.entity()
.map(Integer::parseInt);
return ServerResponse.ok().body(body, Integer.class);
return EntityResponse.fromPublisher(intMono, Integer.class).build();
});
RouterFunction<Integer> result = routerFunction.filter(filterFunction);
RouterFunction<EntityResponse<Mono<Integer>>> result = routerFunction.filter(filterFunction);
assertNotNull(result);
MockServerRequest request = MockServerRequest.builder().build();
Mono<? extends ServerResponse<Integer>> responseMono =
Mono<EntityResponse<Mono<Integer>>> responseMono =
result.route(request).then(hf -> hf.handle(request));
StepVerifier.create(responseMono)
.consumeNextWith(
serverResponse -> {
StepVerifier.create(serverResponse.body())
StepVerifier.create(serverResponse.entity())
.expectNext(42)
.expectComplete()
.verify();
@ -121,6 +119,5 @@ public class RouterFunctionTests {
.expectComplete()
.verify();
}
*/
}