Provide matched pattern information in WebFlux fn

This commit stores the first matching path pattern in the attribute
`RouterFunctions.MATCHING_PATTERN_ATTRIBUTE`.

Issue: SPR-17098
This commit is contained in:
Arjen Poutsma
2018-09-17 14:49:40 +02:00
parent 51f7a3e40f
commit 88ab911cf1
3 changed files with 98 additions and 63 deletions

View File

@@ -30,6 +30,7 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -355,6 +356,31 @@ public abstract class RequestPredicates {
request.attributes().putAll(attributes);
}
private static Map<String, String> mergePathVariables(Map<String, String> oldVariables,
Map<String, String> newVariables) {
if (!newVariables.isEmpty()) {
Map<String, String> mergedVariables = new LinkedHashMap<>(oldVariables);
mergedVariables.putAll(newVariables);
return mergedVariables;
}
else {
return oldVariables;
}
}
private static String mergePatterns(@Nullable String oldPattern, String newPattern) {
if (oldPattern != null) {
if (oldPattern.endsWith("/") && newPattern.startsWith("/")) {
oldPattern = oldPattern.substring(0, oldPattern.length() - 1);
}
return oldPattern + newPattern;
}
else {
return newPattern;
}
}
private static class HttpMethodPredicate implements RequestPredicate {
@@ -403,9 +429,10 @@ public abstract class RequestPredicates {
public boolean test(ServerRequest request) {
PathContainer pathContainer = request.pathContainer();
PathPattern.PathMatchInfo info = this.pattern.matchAndExtract(pathContainer);
traceMatch("Pattern", this.pattern.getPatternString(), request.path(), info != null);
String patternString = this.pattern.getPatternString();
traceMatch("Pattern", patternString, request.path(), info != null);
if (info != null) {
mergeTemplateVariables(request, info.getUriVariables());
mergeAttributes(request, info.getUriVariables(), patternString);
return true;
}
else {
@@ -413,20 +440,22 @@ public abstract class RequestPredicates {
}
}
private static void mergeAttributes(ServerRequest request, Map<String, String> variables,
String pattern) {
Map<String, String> pathVariables = mergePathVariables(request.pathVariables(), variables);
request.attributes().put(RouterFunctions.URI_TEMPLATE_VARIABLES_ATTRIBUTE,
Collections.unmodifiableMap(pathVariables));
pattern = mergePatterns(
(String) request.attributes().get(RouterFunctions.MATCHING_PATTERN_ATTRIBUTE),
pattern);
request.attributes().put(RouterFunctions.MATCHING_PATTERN_ATTRIBUTE, pattern);
}
@Override
public Optional<ServerRequest> nest(ServerRequest request) {
return Optional.ofNullable(this.pattern.matchStartOfPath(request.pathContainer()))
.map(info -> new SubPathServerRequestWrapper(request, info));
}
private void mergeTemplateVariables(ServerRequest request, Map<String, String> variables) {
if (!variables.isEmpty()) {
Map<String, String> oldVariables = request.pathVariables();
Map<String, String> mergedVariables = new LinkedHashMap<>(oldVariables);
mergedVariables.putAll(variables);
request.attributes().put(RouterFunctions.URI_TEMPLATE_VARIABLES_ATTRIBUTE,
Collections.unmodifiableMap(mergedVariables));
}
.map(info -> new SubPathServerRequestWrapper(request, info, this.pattern.getPatternString()));
}
@Override
@@ -601,23 +630,29 @@ public abstract class RequestPredicates {
private final ServerRequest request;
private final PathContainer subPathContainer;
private final PathContainer pathContainer;
private final Map<String, String> pathVariables;
private final Map<String, Object> attributes;
public SubPathServerRequestWrapper(ServerRequest request, PathPattern.PathRemainingMatchInfo info) {
public SubPathServerRequestWrapper(ServerRequest request,
PathPattern.PathRemainingMatchInfo info, String pattern) {
this.request = request;
this.subPathContainer = new SubPathContainer(info.getPathRemaining());
this.pathVariables = mergePathVariables(request, info.getUriVariables());
this.pathContainer = new SubPathContainer(info.getPathRemaining());
this.attributes = mergeAttributes(request, info.getUriVariables(), pattern);
}
private static Map<String, String> mergePathVariables(ServerRequest request,
Map<String, String> pathVariables) {
private static Map<String, Object> mergeAttributes(ServerRequest request,
Map<String, String> pathVariables, String pattern) {
Map<String, Object> result = new ConcurrentHashMap<>(request.attributes());
Map<String, String> result = new LinkedHashMap<>(request.pathVariables());
result.putAll(pathVariables);
return Collections.unmodifiableMap(result);
result.put(RouterFunctions.URI_TEMPLATE_VARIABLES_ATTRIBUTE,
mergePathVariables(request.pathVariables(), pathVariables));
pattern = mergePatterns(
(String) request.attributes().get(RouterFunctions.MATCHING_PATTERN_ATTRIBUTE),
pattern);
result.put(RouterFunctions.MATCHING_PATTERN_ATTRIBUTE, pattern);
return result;
}
@Override
@@ -642,12 +677,12 @@ public abstract class RequestPredicates {
@Override
public String path() {
return this.subPathContainer.value();
return this.pathContainer.value();
}
@Override
public PathContainer pathContainer() {
return this.subPathContainer;
return this.pathContainer;
}
@Override
@@ -700,14 +735,9 @@ public abstract class RequestPredicates {
return this.request.bodyToFlux(typeReference);
}
@Override
public Optional<Object> attribute(String name) {
return this.request.attribute(name);
}
@Override
public Map<String, Object> attributes() {
return this.request.attributes();
return this.attributes;
}
@Override
@@ -721,8 +751,11 @@ public abstract class RequestPredicates {
}
@Override
@SuppressWarnings("unchecked")
public Map<String, String> pathVariables() {
return this.pathVariables;
return (Map<String, String>) this.attributes.getOrDefault(
RouterFunctions.URI_TEMPLATE_VARIABLES_ATTRIBUTE, Collections.emptyMap());
}
@Override

View File

@@ -16,8 +16,6 @@
package org.springframework.web.reactive.function.server;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
@@ -71,6 +69,14 @@ public abstract class RouterFunctions {
public static final String URI_TEMPLATE_VARIABLES_ATTRIBUTE =
RouterFunctions.class.getName() + ".uriTemplateVariables";
/**
* Name of the {@link ServerWebExchange#getAttributes() attribute} that
* contains the matching pattern.
*/
public static final String MATCHING_PATTERN_ATTRIBUTE =
RouterFunctions.class.getName() + ".matchingPattern";
private static final HandlerFunction<ServerResponse> NOT_FOUND_HANDLER =
request -> ServerResponse.notFound().build();
@@ -934,25 +940,13 @@ public abstract class RouterFunctions {
}
return this.routerFunction.route(nestedRequest)
.doOnNext(match -> {
mergeTemplateVariables(serverRequest, nestedRequest.pathVariables());
serverRequest.attributes().clear();
serverRequest.attributes().putAll(nestedRequest.attributes());
});
}
).orElseGet(Mono::empty);
}
@SuppressWarnings("unchecked")
private void mergeTemplateVariables(ServerRequest request, Map<String, String> variables) {
if (!variables.isEmpty()) {
Map<String, Object> attributes = request.attributes();
Map<String, String> oldVariables =
(Map<String, String>) request.attribute(RouterFunctions.URI_TEMPLATE_VARIABLES_ATTRIBUTE)
.orElseGet(LinkedHashMap::new);
Map<String, String> mergedVariables = new LinkedHashMap<>(oldVariables);
mergedVariables.putAll(variables);
attributes.put(RouterFunctions.URI_TEMPLATE_VARIABLES_ATTRIBUTE,
Collections.unmodifiableMap(mergedVariables));
}
}
@Override
public void accept(Visitor visitor) {