I want to determine the uri of the specified route at runtime,maybe depends on parameters or others. How to do it , a example code will be grateful.
You can write custom GatewayFilte.
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String requestPath = request.getPath().pathWithinApplication().value();
......
}
I found I can write a customer GatewayFilter whose order is before NettyRoutingFilter, in filter method I set GATEWAY_REQUEST_URL_ATTR at runtime to change route uri.
public class CustomerFilter implements GatewayFilter, Ordered {
@Override
public int getOrder() {
return 10001;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
try {
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, new URI("https://www.google.com"));
} catch (URISyntaxException e) {
e.printStackTrace();
}
return chain.filter(exchange);
}
}
Can someone tell me whether it is wise to do in this way? Any suggestions are grateful!
Actually there are a lot of attributes you can use.
For example I've write a filter to work as zuul.
@Component
public class UriHostPlaceholderFilter extends AbstractGatewayFilterFactory {
@Override
public GatewayFilter apply(Object config) {
return (exchange, chain) -> {
Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR); // 获取路由。
PathMatchInfo variables = exchange.getAttribute(URI_TEMPLATE_VARIABLES_ATTRIBUTE); // 获取路径变量。
if ((variables == null) || (route == null)) {
return chain.filter(exchange);
}
Map<String, String> uriVariables = variables.getUriVariables();
URI uri = route.getUri();
String host = uri.getHost();
if ((host != null) && uriVariables.containsKey(host)) { // 替换uri中的host。
host = uriVariables.get(host);
}
if (host == null) {
return chain.filter(exchange);
}
URI newUri = UriComponentsBuilder.fromUri(uri).host(host).build().toUri(); // 重构URI。
Route newRoute = Route.builder()
.id(route.getId())
.uri(newUri)
.order(route.getOrder())
.predicate(route.getPredicate())
.filters(route.getFilters())
.build(); // 重构路由。
exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, newRoute);
return chain.filter(exchange);
};
}
}
spring:
cloud:
gateway:
discovery:
locator:
enabled: true
routes:
- id: framework
predicates:
- Path=/{application}/**
uri: lb://application
filters:
- StripPrefix=1
- UriHostPlaceholderFilter
How can the filter be setup via RouteLocatorBuilder. It seems uri() is mandatory here.
@dave-fl it is currently required. You can put a uri like no://op to show that it doesn't matter. I'm thinking of an alias in the javadsl that would help. Not sure about the properties since this needs to be opt-in.
Thank you @spencergibb it seems that the DSL should allow for the AsyncBuilder to be generated after the call to .filters() (without uri()) which it currently does not. Will use the no://op approach for now.
Is exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, newRoute) the suggested approach to set the URI dynamically from the filter?
GATEWAY_REQUEST_URL_ATTR is the proper attribute
Ok, this works with no://op.
The missing sauce was having to set a value for order greater than RouteToRequestUrlFilter .ROUTE_TO_URL_FILTER_ORDER.
@Override public GatewayFilter apply(Object config) { return (exchange, chain) -> { Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR); // 获取路由。 PathMatchInfo variables = exchange.getAttribute(URI_TEMPLATE_VARIABLES_ATTRIBUTE); // 获取路径变量。 if ((variables == null) || (route == null)) { return chain.filter(exchange); } Map
uriVariables = variables.getUriVariables(); URI uri = route.getUri(); String host = uri.getHost(); if ((host != null) && uriVariables.containsKey(host)) { // 替换uri中的host。 host = uriVariables.get(host); } if (host == null) { return chain.filter(exchange); } URI newUri = UriComponentsBuilder.fromUri(uri).host(host).build().toUri(); // 重构URI。 Route newRoute = Route.builder() .id(route.getId()) .uri(newUri) .order(route.getOrder()) .predicate(route.getPredicate()) .filters(route.getFilters()) .build(); // 重构路由。 exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, newRoute); return chain.filter(exchange); }; }
.predicate(route.getPredicate()) has proteced access, so can not visited except for put under the same package
I have a similar requirement and i am able to dynamically create based on input path. I also have to add rewritePath dynamically. How can i use RewritePath within above mentioned code . I don't want to write multiple Rewritepath within my application.yml rather make it dynamic the way i make uri dynamic.
Hope you understand my query and respond asap.
TIA
Please see my query and respond @spencergibb
Thank you for all your posts. I have a case not mentionned here : what if we want to replace the lb://maplaceholder by a placeholder value.
I have tried to do this but i'm facing a URISyntaxException because lb://maplaceholder. That's normal because it's is not, but i want to evaluate this lb://maplaceholder, before the processing of RouteToRequestUrlFilter and LoadBalancerClientFilter
@laugues https://github.com/spring-cloud/spring-cloud-gateway/pull/286#issuecomment-520287269 I hope this will help some!
Hi,
I have a gateway url
https://api-gateway.com/SERVICE-ONE-EUREKA-ID/path/remainingpath
https://api-gateway.com/SERVICE-TWO-EUREKA-ID/path/remainingpath
etc
I want to dynamically call the EUREKA via load balancer. and pass the remaining path using a host predicate.
Here is a implementation that works well for me.
Below is my App.yml
spring:
application:
name: API-GATEWAY
cloud:
gateway:
routes:
- id: common-gateway
predicates:
- Host= **.api-gateway.com
filters:
- StripPrefix=1
- UriHostPlaceholderFilter=10001
uri: no://op
Below is my Code for custom filter
`package com.example.apigateway.filters;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.addOriginalRequestUrl;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.OrderedGatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
@Component
public class UriHostPlaceholderFilter extends AbstractGatewayFilterFactory
public UriHostPlaceholderFilter() {
super(Config.class);
}
public static class Config {
public Config(){
}
public Config(int order){
this.order =order;
}
private int order;
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("order");
}
@Override
public GatewayFilter apply(Config config) {
return new OrderedGatewayFilter((exchange, chain) -> {
String serivceID = "";
String downStreamPath ="";
URI uri = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
LinkedHashSet<URI> originalURI = exchange
.getRequiredAttribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
addOriginalRequestUrl(exchange, uri);
String path = originalURI.stream().findFirst().get().getPath();
Pattern pattern = Pattern.compile("/(?<eurekaSerivceId>[^/]*)/(?<downStreamPath>.*)");
if (path != null) {
Matcher matcher = pattern.matcher(path);
boolean flag = matcher.matches();
serivceID = matcher.group("eurekaSerivceId");
downStreamPath = matcher.group("downStreamPath");
}
String newurl = "lb://"+serivceID.toUpperCase()+downStreamPath;
URI newUri =null;
try {
newUri = new URI(newurl);
} catch (URISyntaxException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, newUri);
return chain.filter(exchange);
},config.getOrder());
}
}
`
Most helpful comment
Ok, this works with
no://op.The missing sauce was having to set a value for order greater than
RouteToRequestUrlFilter .ROUTE_TO_URL_FILTER_ORDER.