问题描述
我正在编写一个API网关,该网关必须基于MAC地址路由请求。端点示例:
/api/v2/device/AABBCCDDEEFF
/api/v2/device/AABBCCDDEEFF/Metadata
/api/v2/device/search?deviceid=AABBCCDDEEFF
我写了一个Custom Predicate Factory,它提取了MAC地址,执行必要的逻辑以确定MAC地址应该路由到哪个URL,然后将该信息存储在ServerWebExchange
属性中。
public class CustomroutePredicateFactory extends AbstractRoutePredicateFactory<CustomroutePredicateFactory.Config> {
// Fields/Constructors Omitted
private static final String IP_ATTRIBUTE = "assignedIp";
private static final String MAC_ATTRIBUTE = "mac";
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return (ServerWebExchange exchange) -> {
String mac = exchange.getAttributes().get(MAC_ATTRIBUTE);
if(mac == null){
mac = extractMacAddress(exchange);
}
if(!exchange.getAttributes().contains(IP_ATTRIBUTE)){
exchange.getAttributes().put(IP_ATTRIBUTE,findAssignedIp(mac);
}
return config.getRouteIp().equals(exchange.getAttribute(IP_ATTRIBUTE));
});
}
// Config Class & utility methods omitted
}
注意:为简便起见,此实现已大大简化
通过这种实现,我可以保证MAC仅提取一次,并且确定请求属于哪个URL的逻辑仅执行一次。对谓词工厂的第一次调用将提取并设置ServerWebExchange属性上的信息,对谓词工厂的任何进一步调用将检测这些属性并使用它们来确定它们是否匹配。
这行得通,但并不是特别整洁。如果我可以某种方式对进入网关之前的每个单个请求设置Exchange属性,应用程序将尝试匹配路由,则将变得更加简单。然后,筛选器可能是一个简单的谓词,用于检查Exchange属性是否相等。
我已经阅读了几次文档,但是似乎没有任何可能。过滤器始终作用于特定路由,并且仅在路由匹配后才运行。可能有可能使第一个路由成为另一个执行必要代码,设置期望属性并始终返回false的谓词,但我可以保证此谓词始终始终先运行 ?似乎应该为这种用例提供支持,但是我一生无法找到一种看起来像hack的方法。有什么想法吗?
解决方法
我认为您的方法很有意义,因为您希望它在过滤器之前运行。
您是否考虑过使用带有GlobalFilter
的订单?您可以确保它始终是第一个运行的过滤器。您还可以通过更改请求并在交换机上设置ServerWebExchange
属性来修改GATEWAY_REQUEST_URL_ATTR
中的URL。
以PrefixPathGatewayFilterFactory
为例,了解如何更改路由到的URI。
您可以通过实现org.springframework.core.Ordered
界面在全局过滤器上设置订单。
话虽这么说,感觉还是有点像黑客,但这是另一种方法。
,我认为重写类RoutePredicateHandlerMapping可能会对您有所帮助。
参见:org.springframework.web.reactive.handler.AbstractHandlerMapping#getHandler
使用 WebFilter
而不是 GatewayFilter
或 GlobalFilter
。它们仅在谓词链之后应用。而 WebFilter
用作拦截器。
@Component
public class CustomRoutePredicateFactory implements WebFilter,Ordered {
private static final String IP_ATTRIBUTE = "assignedIp";
private static final String MAC_ATTRIBUTE = "mac";
@Override
public Mono<Void> filter(ServerWebExchange exchange,WebFilterChain chain) {
String mac = (String) exchange.getAttributes().computeIfAbsent(MAC_ATTRIBUTE,key -> extractMacAddress(exchange));
exchange.getAttributes().computeIfAbsent(IP_ATTRIBUTE,key -> findAssignedIp(mac));
return chain.filter(exchange);
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}