Search code examples
springspring-cloudnetflix-zuulspring-cloud-netflix

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


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


Solution

  • 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;
        }
    }