Updates config suppor for HandlerFunctions to accept RouteProperties
Fixes gh-3188
This commit is contained in:
@@ -185,16 +185,18 @@ public class GatewayMvcPropertiesBeanDefinitionRegistrar implements ImportBeanDe
|
||||
// TODO: cache?
|
||||
// translate handlerFunction
|
||||
String scheme = routeProperties.getUri().getScheme();
|
||||
Map<String, String> handlerArgs = new HashMap<>();
|
||||
// TODO: avoid hardcoded scheme/uri args
|
||||
// maybe find empty args or single RouteProperties param?
|
||||
if (scheme.equals("lb")) {
|
||||
handlerArgs.put("uri", routeProperties.getUri().toString());
|
||||
}
|
||||
Map<String, Object> handlerArgs = new HashMap<>();
|
||||
Optional<NormalizedOperationMethod> handlerOperationMethod = findOperation(handlerOperations,
|
||||
scheme.toLowerCase(), handlerArgs);
|
||||
if (handlerOperationMethod.isEmpty()) {
|
||||
throw new IllegalStateException("Unable to find HandlerFunction for scheme: " + scheme);
|
||||
// single RouteProperties param
|
||||
handlerArgs.clear();
|
||||
String routePropsKey = StringUtils.uncapitalize(RouteProperties.class.getSimpleName());
|
||||
handlerArgs.put(routePropsKey, routeProperties);
|
||||
handlerOperationMethod = findOperation(handlerOperations, scheme.toLowerCase(), handlerArgs);
|
||||
if (handlerOperationMethod.isEmpty()) {
|
||||
throw new IllegalStateException("Unable to find HandlerFunction for scheme: " + scheme);
|
||||
}
|
||||
}
|
||||
NormalizedOperationMethod normalizedOpMethod = handlerOperationMethod.get();
|
||||
Object response = invokeOperation(normalizedOpMethod, normalizedOpMethod.getNormalizedArgs());
|
||||
@@ -215,21 +217,21 @@ public class GatewayMvcPropertiesBeanDefinitionRegistrar implements ImportBeanDe
|
||||
MultiValueMap<String, OperationMethod> predicateOperations = predicateDiscoverer.getOperations();
|
||||
final AtomicReference<RequestPredicate> predicate = new AtomicReference<>();
|
||||
|
||||
routeProperties.getPredicates()
|
||||
.forEach(predicateProperties -> translate(predicateOperations, predicateProperties.getName(),
|
||||
predicateProperties.getArgs(), RequestPredicate.class, requestPredicate -> {
|
||||
log.trace(LogMessage.format("Adding predicate to route %s - %s", routeId,
|
||||
predicateProperties));
|
||||
if (predicate.get() == null) {
|
||||
predicate.set(requestPredicate);
|
||||
}
|
||||
else {
|
||||
RequestPredicate combined = predicate.get().and(requestPredicate);
|
||||
predicate.set(combined);
|
||||
}
|
||||
log.trace(LogMessage.format("Combined predicate for route %s - %s", routeId,
|
||||
predicate.get()));
|
||||
}));
|
||||
routeProperties.getPredicates().forEach(predicateProperties -> {
|
||||
Map<String, Object> args = new LinkedHashMap<>(predicateProperties.getArgs());
|
||||
translate(predicateOperations, predicateProperties.getName(), args, RequestPredicate.class,
|
||||
requestPredicate -> {
|
||||
log.trace(LogMessage.format("Adding predicate to route %s - %s", routeId, predicateProperties));
|
||||
if (predicate.get() == null) {
|
||||
predicate.set(requestPredicate);
|
||||
}
|
||||
else {
|
||||
RequestPredicate combined = predicate.get().and(requestPredicate);
|
||||
predicate.set(combined);
|
||||
}
|
||||
log.trace(LogMessage.format("Combined predicate for route %s - %s", routeId, predicate.get()));
|
||||
});
|
||||
});
|
||||
|
||||
// combine predicate and handlerFunction
|
||||
builder.route(predicate.get(), handlerFunction);
|
||||
@@ -237,8 +239,10 @@ public class GatewayMvcPropertiesBeanDefinitionRegistrar implements ImportBeanDe
|
||||
|
||||
// translate filters
|
||||
MultiValueMap<String, OperationMethod> filterOperations = filterDiscoverer.getOperations();
|
||||
routeProperties.getFilters().forEach(filterProperties -> translate(filterOperations, filterProperties.getName(),
|
||||
filterProperties.getArgs(), HandlerFilterFunction.class, builder::filter));
|
||||
routeProperties.getFilters().forEach(filterProperties -> {
|
||||
Map<String, Object> args = new LinkedHashMap<>(filterProperties.getArgs());
|
||||
translate(filterOperations, filterProperties.getName(), args, HandlerFilterFunction.class, builder::filter);
|
||||
});
|
||||
|
||||
builder.withAttribute(MvcUtils.GATEWAY_ROUTE_ID_ATTR, routeId);
|
||||
|
||||
@@ -246,7 +250,7 @@ public class GatewayMvcPropertiesBeanDefinitionRegistrar implements ImportBeanDe
|
||||
}
|
||||
|
||||
private <T> void translate(MultiValueMap<String, OperationMethod> operations, String operationName,
|
||||
Map<String, String> operationArgs, Class<T> returnType, Consumer<T> operationHandler) {
|
||||
Map<String, Object> operationArgs, Class<T> returnType, Consumer<T> operationHandler) {
|
||||
String normalizedName = StringUtils.uncapitalize(operationName);
|
||||
Optional<NormalizedOperationMethod> operationMethod = findOperation(operations, normalizedName, operationArgs);
|
||||
if (operationMethod.isPresent()) {
|
||||
@@ -263,14 +267,14 @@ public class GatewayMvcPropertiesBeanDefinitionRegistrar implements ImportBeanDe
|
||||
}
|
||||
|
||||
private Optional<NormalizedOperationMethod> findOperation(MultiValueMap<String, OperationMethod> operations,
|
||||
String operationName, Map<String, String> operationArgs) {
|
||||
String operationName, Map<String, Object> operationArgs) {
|
||||
return operations.getOrDefault(operationName, Collections.emptyList()).stream()
|
||||
.map(operationMethod -> new NormalizedOperationMethod(operationMethod, operationArgs))
|
||||
.filter(opeMethod -> matchOperation(opeMethod, operationArgs)).findFirst();
|
||||
}
|
||||
|
||||
private static boolean matchOperation(NormalizedOperationMethod operationMethod, Map<String, String> args) {
|
||||
Map<String, String> normalizedArgs = operationMethod.getNormalizedArgs();
|
||||
private static boolean matchOperation(NormalizedOperationMethod operationMethod, Map<String, Object> args) {
|
||||
Map<String, Object> normalizedArgs = operationMethod.getNormalizedArgs();
|
||||
OperationParameters parameters = operationMethod.getParameters();
|
||||
if (operationMethod.isConfigurable()) {
|
||||
// this is a special case
|
||||
@@ -288,7 +292,7 @@ public class GatewayMvcPropertiesBeanDefinitionRegistrar implements ImportBeanDe
|
||||
return true;
|
||||
}
|
||||
|
||||
private <T> T invokeOperation(OperationMethod operationMethod, Map<String, String> operationArgs) {
|
||||
private <T> T invokeOperation(OperationMethod operationMethod, Map<String, Object> operationArgs) {
|
||||
Map<String, Object> args = new HashMap<>();
|
||||
if (operationMethod.isConfigurable()) {
|
||||
OperationParameter operationParameter = operationMethod.getParameters().get(0);
|
||||
|
||||
@@ -35,13 +35,13 @@ public class NormalizedOperationMethod implements OperationMethod {
|
||||
|
||||
private final OperationMethod delegate;
|
||||
|
||||
private final Map<String, String> normalizedArgs;
|
||||
private final Map<String, Object> normalizedArgs;
|
||||
|
||||
/**
|
||||
* Create a new {@link DefaultOperationMethod} instance.
|
||||
* @param method the source method
|
||||
*/
|
||||
public NormalizedOperationMethod(OperationMethod delegate, Map<String, String> args) {
|
||||
public NormalizedOperationMethod(OperationMethod delegate, Map<String, Object> args) {
|
||||
this.delegate = delegate;
|
||||
normalizedArgs = normalizeArgs(args);
|
||||
}
|
||||
@@ -61,31 +61,36 @@ public class NormalizedOperationMethod implements OperationMethod {
|
||||
return delegate.getParameters();
|
||||
}
|
||||
|
||||
public Map<String, String> getNormalizedArgs() {
|
||||
public Map<String, Object> getNormalizedArgs() {
|
||||
return normalizedArgs;
|
||||
}
|
||||
|
||||
private Map<String, String> normalizeArgs(Map<String, String> operationArgs) {
|
||||
@Override
|
||||
public String toString() {
|
||||
return delegate.toString();
|
||||
}
|
||||
|
||||
private Map<String, Object> normalizeArgs(Map<String, Object> operationArgs) {
|
||||
if (hasGeneratedKey(operationArgs)) {
|
||||
Shortcut shortcut = getMethod().getAnnotation(Shortcut.class);
|
||||
if (shortcut != null) {
|
||||
String[] fieldOrder = getFieldOrder(shortcut);
|
||||
return switch (shortcut.type()) {
|
||||
case DEFAULT -> {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
int entryIdx = 0;
|
||||
for (Map.Entry<String, String> entry : operationArgs.entrySet()) {
|
||||
for (Map.Entry<String, Object> entry : operationArgs.entrySet()) {
|
||||
String key = normalizeKey(entry.getKey(), entryIdx, operationArgs, fieldOrder);
|
||||
// TODO: support spel?
|
||||
// getValue(parser, beanFactory, entry.getValue());
|
||||
String value = entry.getValue();
|
||||
Object value = entry.getValue();
|
||||
map.put(key, value);
|
||||
entryIdx++;
|
||||
}
|
||||
yield map;
|
||||
}
|
||||
case LIST -> {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
// field order should be of size 1
|
||||
Assert.isTrue(fieldOrder != null && fieldOrder.length == 1,
|
||||
"Shortcut Configuration Type GATHER_LIST must have shortcutFieldOrder of size 1");
|
||||
@@ -110,11 +115,11 @@ public class NormalizedOperationMethod implements OperationMethod {
|
||||
return fieldOrder;
|
||||
}
|
||||
|
||||
private static boolean hasGeneratedKey(Map<String, String> operationArgs) {
|
||||
private static boolean hasGeneratedKey(Map<String, Object> operationArgs) {
|
||||
return operationArgs.keySet().stream().anyMatch(key -> key.startsWith(NameUtils.GENERATED_NAME_PREFIX));
|
||||
}
|
||||
|
||||
static String normalizeKey(String key, int entryIdx, Map<String, String> args, String[] fieldOrder) {
|
||||
static String normalizeKey(String key, int entryIdx, Map<String, Object> args, String[] fieldOrder) {
|
||||
// RoutePredicateFactory has name hints and this has a fake key name
|
||||
// replace with the matching key hint
|
||||
if (key.startsWith(NameUtils.GENERATED_NAME_PREFIX) && fieldOrder.length > 0 && entryIdx < args.size()
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.cloud.gateway.server.mvc.config.RouteProperties;
|
||||
import org.springframework.cloud.gateway.server.mvc.handler.HandlerDiscoverer;
|
||||
import org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions;
|
||||
import org.springframework.cloud.gateway.server.mvc.handler.HandlerSupplier;
|
||||
@@ -33,6 +34,10 @@ public class LoadBalancerHandlerSupplier implements HandlerSupplier {
|
||||
return Arrays.asList(getClass().getMethods());
|
||||
}
|
||||
|
||||
public static HandlerDiscoverer.Result lb(RouteProperties routeProperties) {
|
||||
return lb(routeProperties.getUri());
|
||||
}
|
||||
|
||||
public static HandlerDiscoverer.Result lb(URI uri) {
|
||||
// TODO: how to do something other than http
|
||||
return new HandlerDiscoverer.Result(HandlerFunctions.http(),
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
import jakarta.servlet.ServletException;
|
||||
|
||||
import org.springframework.cloud.gateway.server.mvc.common.MvcUtils;
|
||||
import org.springframework.cloud.gateway.server.mvc.config.RouteProperties;
|
||||
import org.springframework.web.servlet.function.HandlerFunction;
|
||||
import org.springframework.web.servlet.function.ServerRequest;
|
||||
import org.springframework.web.servlet.function.ServerResponse;
|
||||
@@ -36,6 +37,10 @@ public abstract class HandlerFunctions {
|
||||
|
||||
}
|
||||
|
||||
public static HandlerFunction<ServerResponse> forward(RouteProperties routeProperties) {
|
||||
return forward(routeProperties.getUri().getPath());
|
||||
}
|
||||
|
||||
public static HandlerFunction<ServerResponse> forward(String path) {
|
||||
// ok() is wrong, but can be overridden by the forwarded request.
|
||||
return request -> GatewayServerResponse.ok().build((httpServletRequest, httpServletResponse) -> {
|
||||
|
||||
@@ -45,7 +45,7 @@ spring.cloud.gateway.mvc:
|
||||
name: X-Test
|
||||
values: listRoute3
|
||||
- id: listRoute4
|
||||
uri: https://example1.com
|
||||
uri: forward:/mycontroller
|
||||
predicates:
|
||||
- Path=/anything/example1
|
||||
filters:
|
||||
|
||||
Reference in New Issue
Block a user