Introduce RouterFunction visitor
This commit introduces a visitor for router functions (RouterFunctions.Visitor), allowing to iterate over all the components that make up a router function. This commit also introduces a ToStringVisitor, which creates a nicely formatted string for use with toString(). Issue: SPR-15711, SPR-15711
This commit is contained in:
parent
7b6f1d1b58
commit
2841ef5d05
|
@ -155,4 +155,8 @@ class PathResourceLookupFunction implements Function<ServerRequest, Mono<Resourc
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s -> %s", this.pattern, this.location);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,8 +47,7 @@ public interface RouterFunction<T extends ServerResponse> {
|
|||
* @see #andOther(RouterFunction)
|
||||
*/
|
||||
default RouterFunction<T> and(RouterFunction<T> other) {
|
||||
return request -> this.route(request)
|
||||
.switchIfEmpty(Mono.defer(() -> other.route(request)));
|
||||
return new RouterFunctions.SameComposedRouterFunction<>(this, other);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -61,10 +60,7 @@ public interface RouterFunction<T extends ServerResponse> {
|
|||
* @see #and(RouterFunction)
|
||||
*/
|
||||
default RouterFunction<?> andOther(RouterFunction<?> other) {
|
||||
return request -> this.route(request)
|
||||
.map(RouterFunctions::cast)
|
||||
.switchIfEmpty(
|
||||
Mono.defer(() -> other.route(request).map(RouterFunctions::cast)));
|
||||
return new RouterFunctions.DifferentComposedRouterFunction(this, other);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -105,7 +101,18 @@ public interface RouterFunction<T extends ServerResponse> {
|
|||
* @return the filtered routing function
|
||||
*/
|
||||
default <S extends ServerResponse> RouterFunction<S> filter(HandlerFilterFunction<T, S> filterFunction) {
|
||||
return request -> this.route(request).map(filterFunction::apply);
|
||||
return new RouterFunctions.FilteredRouterFunction<>(this, filterFunction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accept the given visitor. Default implementation calls
|
||||
* {@link RouterFunctions.Visitor#unknown(RouterFunction)}; composed {@code RouterFunction}
|
||||
* implementations are expected to call {@code accept} for all components that make up this
|
||||
* router function
|
||||
* @param visitor the visitor to accept
|
||||
*/
|
||||
default void accept(RouterFunctions.Visitor visitor) {
|
||||
visitor.unknown(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -150,7 +150,7 @@ public abstract class RouterFunctions {
|
|||
*/
|
||||
public static RouterFunction<ServerResponse> resources(Function<ServerRequest, Mono<Resource>> lookupFunction) {
|
||||
Assert.notNull(lookupFunction, "'lookupFunction' must not be null");
|
||||
return request -> lookupFunction.apply(request).map(ResourceHandlerFunction::new);
|
||||
return new ResourcesRouterFunction(lookupFunction);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -259,8 +259,138 @@ public abstract class RouterFunctions {
|
|||
return (HandlerFunction<T>) handlerFunction;
|
||||
}
|
||||
|
||||
private static class DefaultRouterFunction<T extends ServerResponse>
|
||||
implements RouterFunction<T> {
|
||||
|
||||
/**
|
||||
* Receives notifications from the logical structure of router functions.
|
||||
*/
|
||||
public interface Visitor {
|
||||
|
||||
/**
|
||||
* Receive notification of the beginning of a nested router function.
|
||||
* @param predicate the predicate that applies to the nested router functions
|
||||
* @see RouterFunctions#nest(RequestPredicate, RouterFunction)
|
||||
*/
|
||||
void startNested(RequestPredicate predicate);
|
||||
|
||||
/**
|
||||
* Receive notification of the end of a nested router function.
|
||||
* @param predicate the predicate that applies to the nested router functions
|
||||
* @see RouterFunctions#nest(RequestPredicate, RouterFunction)
|
||||
*/
|
||||
void endNested(RequestPredicate predicate);
|
||||
|
||||
/**
|
||||
* Receive notification of a standard predicated route to a handler function.
|
||||
* @param predicate the predicate that applies to the handler function
|
||||
* @param handlerFunction the handler function.
|
||||
* @see RouterFunctions#route(RequestPredicate, HandlerFunction)
|
||||
*/
|
||||
void route(RequestPredicate predicate, HandlerFunction<?> handlerFunction);
|
||||
|
||||
/**
|
||||
* Receive notification of a resource router function.
|
||||
* @param lookupFunction the lookup function for the resources
|
||||
* @see RouterFunctions#resources(Function)
|
||||
*/
|
||||
void resources(Function<ServerRequest, Mono<Resource>> lookupFunction);
|
||||
|
||||
/**
|
||||
* Receive notification of an unknown router function. This method is called for router
|
||||
* functions that were not created via the various {@link RouterFunctions} methods.
|
||||
* @param routerFunction the router function
|
||||
*/
|
||||
void unknown(RouterFunction<?> routerFunction);
|
||||
}
|
||||
|
||||
|
||||
private static abstract class AbstractRouterFunction<T extends ServerResponse> implements RouterFunction<T> {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
ToStringVisitor visitor = new ToStringVisitor();
|
||||
accept(visitor);
|
||||
return visitor.toString();
|
||||
}
|
||||
}
|
||||
|
||||
final static class SameComposedRouterFunction<T extends ServerResponse> extends AbstractRouterFunction<T> {
|
||||
|
||||
private final RouterFunction<T> first;
|
||||
|
||||
private final RouterFunction<T> second;
|
||||
|
||||
public SameComposedRouterFunction(RouterFunction<T> first, RouterFunction<T> second) {
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<HandlerFunction<T>> route(ServerRequest request) {
|
||||
return this.first.route(request)
|
||||
.switchIfEmpty(Mono.defer(() -> this.second.route(request)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Visitor visitor) {
|
||||
this.first.accept(visitor);
|
||||
this.second.accept(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
final static class DifferentComposedRouterFunction extends AbstractRouterFunction<ServerResponse> {
|
||||
|
||||
private final RouterFunction<?> first;
|
||||
|
||||
private final RouterFunction<?> second;
|
||||
|
||||
public DifferentComposedRouterFunction(RouterFunction<?> first, RouterFunction<?> second) {
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<HandlerFunction<ServerResponse>> route(ServerRequest request) {
|
||||
return this.first.route(request)
|
||||
.map(RouterFunctions::cast)
|
||||
.switchIfEmpty(Mono.defer(() -> this.second.route(request).map(RouterFunctions::cast)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Visitor visitor) {
|
||||
this.first.accept(visitor);
|
||||
this.second.accept(visitor);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
final static class FilteredRouterFunction<T extends ServerResponse, S extends ServerResponse>
|
||||
implements RouterFunction<S> {
|
||||
|
||||
private final RouterFunction<T> routerFunction;
|
||||
|
||||
private final HandlerFilterFunction<T, S> filterFunction;
|
||||
|
||||
public FilteredRouterFunction(
|
||||
RouterFunction<T> routerFunction,
|
||||
HandlerFilterFunction<T, S> filterFunction) {
|
||||
this.routerFunction = routerFunction;
|
||||
this.filterFunction = filterFunction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<HandlerFunction<S>> route(ServerRequest request) {
|
||||
return this.routerFunction.route(request).map(this.filterFunction::apply);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Visitor visitor) {
|
||||
this.routerFunction.accept(visitor);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class DefaultRouterFunction<T extends ServerResponse>
|
||||
extends AbstractRouterFunction<T> {
|
||||
|
||||
private final RequestPredicate predicate;
|
||||
|
||||
|
@ -287,13 +417,14 @@ public abstract class RouterFunctions {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s -> %s", this.predicate, this.handlerFunction);
|
||||
public void accept(Visitor visitor) {
|
||||
visitor.route(this.predicate, this.handlerFunction);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class DefaultNestedRouterFunction<T extends ServerResponse>
|
||||
implements RouterFunction<T> {
|
||||
private static final class DefaultNestedRouterFunction<T extends ServerResponse>
|
||||
extends AbstractRouterFunction<T> {
|
||||
|
||||
private final RequestPredicate predicate;
|
||||
|
||||
|
@ -322,12 +453,33 @@ public abstract class RouterFunctions {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s -> %s", this.predicate, this.routerFunction);
|
||||
public void accept(Visitor visitor) {
|
||||
visitor.startNested(this.predicate);
|
||||
this.routerFunction.accept(visitor);
|
||||
visitor.endNested(this.predicate);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class ResourcesRouterFunction extends AbstractRouterFunction<ServerResponse> {
|
||||
|
||||
private final Function<ServerRequest, Mono<Resource>> lookupFunction;
|
||||
|
||||
public ResourcesRouterFunction(Function<ServerRequest, Mono<Resource>> lookupFunction) {
|
||||
this.lookupFunction = lookupFunction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<HandlerFunction<ServerResponse>> route(ServerRequest request) {
|
||||
return this.lookupFunction.apply(request).map(ResourceHandlerFunction::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Visitor visitor) {
|
||||
visitor.resources(this.lookupFunction);
|
||||
}
|
||||
}
|
||||
|
||||
private static class HandlerStrategiesResponseContext implements ServerResponse.Context {
|
||||
|
||||
private final HandlerStrategies strategies;
|
||||
|
@ -346,4 +498,5 @@ public abstract class RouterFunctions {
|
|||
return this.strategies.viewResolvers();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* 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 java.util.function.Function;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
/**
|
||||
* Implementation of {@link RouterFunctions.Visitor} that creates a
|
||||
* @author Arjen Poutsma
|
||||
* @since 5.0
|
||||
*/
|
||||
class ToStringVisitor implements RouterFunctions.Visitor {
|
||||
|
||||
private static final String NEW_LINE = System.getProperty("line.separator", "\\n");
|
||||
|
||||
private final StringBuilder builder = new StringBuilder();
|
||||
|
||||
private int indent = 0;
|
||||
|
||||
@Override
|
||||
public void startNested(RequestPredicate predicate) {
|
||||
indent();
|
||||
this.builder.append(predicate);
|
||||
this.builder.append(" => {");
|
||||
this.builder.append(NEW_LINE);
|
||||
this.indent++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endNested(RequestPredicate predicate) {
|
||||
this.indent--;
|
||||
indent();
|
||||
this.builder.append('}');
|
||||
this.builder.append(NEW_LINE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void route(RequestPredicate predicate, HandlerFunction<?> handlerFunction) {
|
||||
indent();
|
||||
this.builder.append(predicate);
|
||||
this.builder.append(" -> ");
|
||||
this.builder.append(handlerFunction);
|
||||
this.builder.append(NEW_LINE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resources(Function<ServerRequest, Mono<Resource>> lookupFunction) {
|
||||
indent();
|
||||
this.builder.append(lookupFunction);
|
||||
this.builder.append(NEW_LINE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unknown(RouterFunction<?> routerFunction) {
|
||||
indent();
|
||||
this.builder.append(routerFunction);
|
||||
}
|
||||
|
||||
private void indent() {
|
||||
for (int i=0; i < this.indent; i++) {
|
||||
this.builder.append(' ');
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String result = this.builder.toString();
|
||||
if (result.endsWith(NEW_LINE)) {
|
||||
result = result.substring(0, result.length() - NEW_LINE.length());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -102,8 +102,8 @@ public class RouterFunctionMapping extends AbstractHandlerMapping implements Ini
|
|||
|
||||
List<RouterFunction<?>> routerFunctions = routerFunctions();
|
||||
if (!CollectionUtils.isEmpty(routerFunctions) && logger.isInfoEnabled()) {
|
||||
routerFunctions.forEach(routerFunction1 -> {
|
||||
logger.info("Mapped " + routerFunction1);
|
||||
routerFunctions.forEach(routerFunction -> {
|
||||
logger.info("Mapped " + routerFunction);
|
||||
});
|
||||
}
|
||||
this.routerFunction = routerFunctions.stream()
|
||||
|
|
Loading…
Reference in New Issue