Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to dynamic determine route uri at runtime? #276

Closed
maoyunfei opened this issue Apr 12, 2018 · 18 comments · Fixed by #286
Closed

How to dynamic determine route uri at runtime? #276

maoyunfei opened this issue Apr 12, 2018 · 18 comments · Fixed by #286

Comments

@maoyunfei
Copy link

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.

@aoslee
Copy link

aoslee commented Apr 13, 2018

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

@maoyunfei
Copy link
Author

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!

@hnxuruochen
Copy link

hnxuruochen commented May 18, 2018

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

@dave-fl
Copy link

dave-fl commented Aug 9, 2018

How can the filter be setup via RouteLocatorBuilder. It seems uri() is mandatory here.

@spencergibb
Copy link
Member

@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.

@dave-fl
Copy link

dave-fl commented Aug 9, 2018

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?

@spencergibb
Copy link
Member

GATEWAY_REQUEST_URL_ATTR is the proper attribute

@dave-fl
Copy link

dave-fl commented Aug 9, 2018

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.

@eagle8625
Copy link

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

.predicate(route.getPredicate()) has proteced access, so can not visited except for put under the same package

@pkrishi
Copy link

pkrishi commented Jan 25, 2019

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

@pkrishi
Copy link

pkrishi commented Jan 25, 2019

Please see my query and respond @spencergibb

@laugues
Copy link

laugues commented Aug 17, 2019

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

@Qsimple
Copy link
Contributor

Qsimple commented Sep 8, 2019

@laugues #286 (comment) I hope this will help some!

@naveenkumarpandian
Copy link

naveenkumarpandian commented Apr 3, 2020

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<UriHostPlaceholderFilter.Config> {

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

}
`

Johnny850807 pushed a commit to Johnny850807/spring-cloud-gateway that referenced this issue Dec 14, 2020
Johnny850807 pushed a commit to Johnny850807/spring-cloud-gateway that referenced this issue Dec 14, 2020
@shiddarthbista
Copy link

Is there a way to set the route uri dynamically based on a value in the request body during runtime?

@gangadharkasturi
Copy link

Is there a way to set the route uri dynamically based on a value in the request body during runtime?

Even I am looking for the same. Did you find the solution?

@backup01
Copy link

backup01 commented Feb 20, 2024

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

.predicate(route.getPredicate()) has proteced access, so can not visited except for put under the same package

I faced with the same issue.
And if i won't set the predicate in Route's builder, it says java.lang.IllegalArgumentException: predicate must not be null.
Is there any solution?

@oldfr
Copy link

oldfr commented Aug 27, 2024

@hnxuruochen ,

Even I am getting error:
java.lang.IllegalArgumentException: predicate must not be null.

Could you help here?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.