Spring-cloud-gateway: How to dynamic determine route uri at runtime?

Created on 12 Apr 2018  ·  14Comments  ·  Source: spring-cloud/spring-cloud-gateway

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.

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.

All 14 comments

You can write custom GatewayFilte.

sample code:
https://github.com/spring-cloud/spring-cloud-gateway/blob/master/spring-cloud-gateway-sample/src/main/java/org/springframework/cloud/gateway/sample/ThrottleGatewayFilter.java

@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

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());
}

}
`

Was this page helpful?
0 / 5 - 0 ratings

Related issues

gaoyf picture gaoyf  ·  3Comments

pravinkumarb84 picture pravinkumarb84  ·  7Comments

aresa7796 picture aresa7796  ·  6Comments

larva2333 picture larva2333  ·  6Comments

xiaozhiliaoo picture xiaozhiliaoo  ·  4Comments