Spring-cloud-gateway: Prefer more specific route when multiple defintions match

Created on 28 Feb 2020  路  9Comments  路  Source: spring-cloud/spring-cloud-gateway

In case when two or more route definitions match, and no order is defined, the first route in the list will always match, even if it has more generic predicates. E.g.

routes:
  - id: one
    uri: https://example.com
    predicates:
      - Path=/test/**

  - id: two
    uri: https://example.org
    predicates:
      - Path=/test/example

When request with path /test/example comes in, route one will match. It would be nice if gateway prefer more specific route two by ordering routes using something like AntPathMatcher.getComparator.

An alternative is always explicitly control order of 'generic' routes, e.g. routes including ** patterns by making sure they always have higher order than specific routes.

feedback-provided

Most helpful comment

Next config will probably solve this case

routes:
  - id: one
    uri: https://example.com
    order: 1
    predicates:
      - Path=/test/**

  - id: two
    uri: https://example.org
    order: 0
    predicates:
      - Path=/test/example

All 9 comments

we can't use the ant path matcher because route predicates can be more than just path (or no path at all).

Next config will probably solve this case

routes:
  - id: one
    uri: https://example.com
    order: 1
    predicates:
      - Path=/test/**

  - id: two
    uri: https://example.org
    order: 0
    predicates:
      - Path=/test/example

@spencergibb thanks, good point. I used Path as an example, I think the same rule (prefer specific over generic predicate) should be applied to all other predicates as well. E.g. Host=**.example.com should be matched after Host=www.example.com.

I think this is achievable at least for all predicates using AntPathMatcher, like Host and Path. Regex based predicates are more tricky. I can put a prototype in a PR if you think the change can be useful in general.

Only host and path use ant matchers, you mentioned regex like header, but what about method or before or any of the other predicates?

I guess these can be ignored. The main problem I'm looking to solve is to guarantee that 'greedy' predicates like /** matched last.

Here is an example. In Animal Rescue application, which is an SPA sample application for Pivotal Spring Cloud Gateway, there are two routes - /rescue/login and /rescue/**. The former has extra security fitlers applied and triggers authentication flow, so it has to be matched before /**. It was originally called /rescue/admin and when I renamed it to /rescue/login authentication broke - because Gateway re-ordered routes and /rescue/** was matched first. There was an easy fix with putting order attribute, but I think if Gateway can just make sure that /rescue/** always matched last, there wouldn't be any need in that.

Gateway shouldn't arbitrarily reorder routes. Ignoring other parts of the predicates is dangerous because those things might actually be part of the precedent.

Gateway shouldn't arbitrarily reorder routes.

Agree, I'm looking into this one separately, could be something with the way how routes are provisioned.

Ignoring other parts of the predicates is dangerous

I meant only ignore them for ordering. Filter by predicates as usual, but then order a list of matching routes by path / host if provided.

I put a little PoC together here to order based on path, but not sure I'm too happy with it:

  • Now route matching is less efficient, multiple routes are matched and then sorted
  • Since actual sorting is happening on request, it may have performance penalty
  • It is hard to write generic ordering logic, so ordering is bound to path / host
  • I had to used metadata to pass path data from route defition

So with all that in mind, and now when I understand much better how route matching works in SCG, I agree it may be not a good idea.

:+1: with the fact that we have an order attribute and I also opened #1606 to investigate relative ordering, I'm going to close this then.

@spencergibb , the issue does not occur only for more specific paths, but also, when more predicates are available; for instance;

- id: service
  uri: ${service.scheme}://${service.host}
  predicates:
    - Path=/api/**
- id: mock_service
  uri: ${mock-service.scheme}://${mock-service.host}
  predicates:
    - Path=/api/**
    - Header=X-mock, \d+

Given that, the Header predicate in the example is useless. The first route will always be chosen because the path predicate matches.

I would agree on improving this by #1606 only for cases in which the number and type of predicates are the same, so, if two routes had only path predicates, that would result in a true evaluation, then rely on the (before|after) concept.

However, for this particular case, I would aim for something different:

  • Compute the weight of the route by counting how many predicates match.
  • Being able to have the logical negation of a predicate such as with spring profiles:
    @Profile("dev")
    @Bean
    fun devBean()

    @Profile("!dev")
    @Bean
    fun notDevBean()

Translated to predicates something like:

- id: service
  uri: ${service.scheme}://${service.host}
  predicates:
    - Path=/api/**
    - Header=!X-mock # Matches when there is no header
- id: mock_service
  uri: ${mock-service.scheme}://${mock-service.host}
  predicates:
    - Path=/api/**
    - Header=X-mock # Matches when the header is present
- id: mock_service_2
  uri: ${mock-service.scheme}://${mock-service.host}
  predicates:
    - Path=/api/**
    - Header=X-mock, \d+ # Matches only when the header matches the regexp
- id: mock_service_3
  uri: ${mock-service.scheme}://${mock-service.host}
  predicates:
    - Path=/api/**
    - Header=!X-mock, \d+ # Matches when there is no header or the regexp doesnt match
Was this page helpful?
0 / 5 - 0 ratings

Related issues

xfworld picture xfworld  路  3Comments

aresa7796 picture aresa7796  路  6Comments

ryanjbaxter picture ryanjbaxter  路  6Comments

NickolaiBarysevich picture NickolaiBarysevich  路  6Comments

vborcea picture vborcea  路  6Comments