Match route in spring zuul based on strict matching
Asked Answered
S

3

6

Summary: ZUUL doesnt pick the right destination url for the input path since it doesnt do strict matching of the input path.

Below is my zuul routes :

zuul:
  routes: 
    auth:
      path: /v1/txn/**
      url: http://localhost:8900/v1/cardhostauth
    cardproduct: 
      path: /v1/customer/card/product/**
      url: http://localhost:8800/v1/customer/card/product
    cardcomposite:
      path: /v1/customer/**
      url: http://localhost:8400/v1/composite

For input path:"/v1/customer/card/product/" , It is expected to choose -http://localhost:8800/v1/customer/card/product but it chooses http://localhost:8400/v1/composite. My expectation was that the path pattern match happens in the order specified and is stricter but seems it doesnt work that way.

Can you let me know how ZUUL works when you have multiple routes defined for similar input path ?

Thx

P.S - I can see this issue when i run via Docker in AWS but the issue doesn't come up when i run from eclipse. Has the order of zuul route depend on the spring zuul jar (spring-cloud-starter-netflix-zuul - 2.0.0.RELEASE vs 2.0.1.RELEASE)

Saffren answered 10/10, 2018 at 7:2 Comment(0)
D
3

For picking the route based on best match, i found this thread helpful (thanks to @maximdim). Basically you can write your own custom router to pick best matching route.

https://github.com/spring-cloud/spring-cloud-netflix/issues/2408

Hope this helps. (And as it's my first answer, my apologies if it is not a good one.)

An example taken from github thread goes as follows:

public class CustomRouteLocator extends SimpleRouteLocator {
    public CustomRouteLocator(String servletPath, ZuulProperties properties) {
        super(servletPath, properties);
    }

    @Override
    protected ZuulRoute getZuulRoute(String adjustedPath) {
        // Spring's AbstractUrlHandlerMapping already found best matching path for us
        // and stored it into request attribute. See AbstractUrlHandlerMapping.exposePathWithinMapping
        RequestContext ctx = RequestContext.getCurrentContext();
        String bestMatchingPattern = (String)ctx.getRequest().getAttribute(HandlerMapping.class.getName() + ".bestMatchingPattern");
        if (bestMatchingPattern == null) {
            return super.getZuulRoute(adjustedPath);
        }

        if (!matchesIgnoredPatterns(adjustedPath)) {
            return locateRoutes().get(bestMatchingPattern);
        }
        return null;
    }
}

@maximdim:

Instead of duplicating logic to find 'best match' I simply getting it from request attribute, where Spring's HandlerMapping saves it. A bit hacky perhaps, but seems to work for me.

Deprecative answered 23/10, 2018 at 11:45 Comment(0)
P
2

Editing answer based on Zuul's official documentation: https://cloud.spring.io/spring-cloud-netflix/multi/multi__router_and_filter_zuul.html

If you need your routes to have their order preserved, you need to use a YAML file, as the ordering is lost when using a properties file. The following example shows such a YAML file:

application.yml.

 zuul:
  routes:
    users:
      path: /myusers/**
    legacy:
      path: /**

If you were to use a properties file, the legacy path might end up in front of the users path, rendering the users path unreachable.

Make sure you're using the latest stable version of Spring Cloud (currently 2.0.2)

Provencal answered 26/10, 2018 at 16:52 Comment(3)
Are you sure this will work with certainity as all always ?Saffren
Edited based on official documentation.Provencal
I had been using yml all along. I did some more analysis and found that the issue doesnt come up when i bring up my service via docker but when i bring it up within Kubernetes cluster and the url points to kube service then it doesnt work. In my case it is appending the input path whereas it works fine when running individually in docker or eclipse.Saffren
M
1

I've been working with zuul since 2 years ago and I think the best approach in order to work with zuul routes is when you are working with some discovery service for example Eureka service who registers your application.

So you just need to add service-id

zuul:
  routes:
    auth: 
      path: /v1/txn/**
      service-id: txn-service
      strip-prefix: false
      sensitiveHeaders:
    cardproduct:
      path: /v1/customer/card/product/**
      service-id: card-service   // here you need to put application name .
      strip-prefix: false
      sensitiveHeaders:
    cardcomposite:
      path: /v1/customer/**
      service-id: customer-service
      strip-prefix: false
      sensitiveHeaders:
Magruder answered 29/10, 2018 at 18:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.