How to make Zuul dynamic routing based on HTTP method and resolve target host by 'serviceId'?
Asked Answered
T

2

5

How to make Zuul dynamic routing based on HTTP method (GET/POST/PUT...)? For example, when you need to route the POST request to the different host instead of the default one described in 'zuul.routes.*'...

zuul:
  routes:
    first-service:
      path: /first/**
      serviceId: first-service
      stripPrefix: false

    second-service:
      path: /second/**
      serviceId: second-service
      stripPrefix: false

I.e. when we request 'GET /first' then Zuul route the request to the 'first-service', but if we request 'POST /first' then Zuul route the request to the 'second-service'.

Tania answered 17/12, 2017 at 15:26 Comment(0)
T
9

To implement dynamic routing based on HTTP method we can create a custom 'route' type ZuulFilter and resolve 'serviceId' through DiscoveryClient. Fore example:

@Component
public class PostFilter extends ZuulFilter {

    private static final String REQUEST_PATH = "/first";
    private static final String TARGET_SERVICE = "second-service";
    private static final String HTTP_METHOD = "POST";

    private final DiscoveryClient discoveryClient;

    public PostOrdersFilter(DiscoveryClient discoveryClient) {
        this.discoveryClient = discoveryClient;
    }

    @Override
    public String filterType() {
        return "route";
    }

    @Override
    public int filterOrder() {
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        RequestContext context = RequestContext.getCurrentContext();
        HttpServletRequest request = context.getRequest();
        String method = request.getMethod();
        String requestURI = request.getRequestURI();
        return HTTP_METHOD.equalsIgnoreCase(method) && requestURI.startsWith(REQUEST_PATH);
    }

    @Override
    public Object run() {

        RequestContext context = RequestContext.getCurrentContext();
        List<ServiceInstance> instances = discoveryClient.getInstances(TARGET_SERVICE);
        try {
            if (instances != null && instances.size() > 0) {
                context.setRouteHost(instances.get(0).getUri().toURL());
            } else {
                throw new IllegalStateException("Target service instance not found!");
            }
        } catch (Exception e) {
            throw new IllegalArgumentException("Couldn't get service URL!", e);
        }
        return null;
    }
}
Tania answered 17/12, 2017 at 15:26 Comment(6)
@Cepro, how do I do it that it will cater not just POST request but all kinds of request? Thanks.Liquate
For 'all kinds of requests' you should simply use the standard Zuul functionality that works for every HTTP methods based on their request path.Tania
ok. By the way, is context.setRouteHost(...) needs the full URL or this just the host name e.g localhost or IP?Liquate
It's a full URL. Try to take a look in doc...Tania
@Cepr0, what about load balancing the second-service? Is it doable that way?Roguery
@Roguery Maybe, if you implement for example Round-robin algorithm. Why not?..Tania
P
0

@Cepr0's solution is right. Here I am proposing just a simpler way (without service discovery). Assuming you have that route:

zuul:
    routes:
        first:
            path: /first/**
            # No need for service id or url

Then you can route requests for '/first' route in 'route' type filter just by setting location to request context.

@Component
    public class RoutingFilter extends ZuulFilter {

    @Override
    public String filterType() {
        return ROUTE_TYPE;
    }

    @Override
    public int filterOrder() {
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        /* Routing logic goes here */
        URL location = getRightLocationForRequest();
        ctx.setRouteHost(location);

        return null;
    }
}
Parenthood answered 20/5, 2020 at 10:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.