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

Routing based on Request Header #360

Closed
BrettJG opened this issue May 16, 2018 · 42 comments · Fixed by #1312 · May be fixed by #1684
Closed

Routing based on Request Header #360

BrettJG opened this issue May 16, 2018 · 42 comments · Fixed by #1312 · May be fixed by #1684
Assignees
Labels
feature A new feature merged Issue has been merged to dev and is waiting for the next release Routing Ocelot feature: Routing Spring'24 Spring 2024 release
Milestone

Comments

@BrettJG
Copy link

BrettJG commented May 16, 2018

Hi,

I'm interested if Ocelot currently supports downstream routing to multiple endpoints based on a Request Header?

A use case for this is something like:

  • Octolot receives a request for /products
  • The request has a header something like Region: AU / Region: UK
  • Based on the Region header field the request is routed to a matching downstream endpoint

eg.
AU redirects to -> api.internal-au/products
UK redirects to -> api.internal-uk/products

cheers

@TomPallister
Copy link
Member

@BrettJG thanks for your interest in the project. Ocelot doesn’t support this at the moment but we could add it as a feature. I’m working on another issue at the moment but when I have finished I’ll take a look at this.

@TomPallister TomPallister added feature A new feature medium effort Likely a few days of development effort help wanted Not actively being worked on. If you plan to contribute, please drop a note. labels May 16, 2018
@netren20000
Copy link

yes, i need it too!
in A/B Testing

@TomPallister
Copy link
Member

@netren20000 cool thanks for letting me know! I will try and do this ASAP! I am very busy at the moment just answering questions and fixing bugs! Hopefully once all the bugs are sorted out I will do this feature!

@iderbyshev
Copy link

iderbyshev commented Aug 1, 2018

Hi @TomPallister .
First I'd like to thank you for this great product.

I'm currently trying to use it in a multi tenant solution which is hosted in Service Fabric.
Based on a tenant id in a HTTP header I need to route to different instances of a particular Service Fabric application (i.e. change backend ServiceName dynamically based on current request parameters).
So I implemented my custom middleware which replaces DownstreamReRoute with a dynamic one and attached it as PreQueryStringBuilderMiddleware :

    public static async Task InvokeAsync(DownstreamContext context, Func<Task> next)
    {
	    var tenantId = <get it from HTTP header>;
                var reRoute = context.DownstreamReRoute;
                var tenantServiceName = $"{tenantId}.{reRoute.ServiceName}";

                context.DownstreamReRoute = new DownstreamReRoute(
                    reRoute.Key,
                    reRoute.UpstreamPathTemplate,
                    reRoute.UpstreamHeadersFindAndReplace,
                    reRoute.DownstreamHeadersFindAndReplace,
                    reRoute.DownstreamAddresses,
                    tenantServiceName,
		...
                );
            }
        }
        await next.Invoke();
    }

Though it works like a charm, looks like a hack.
So it would be great if we could use placeholders in ServiceName (and Host maybe), and these placeholders could be set from request parameters (HTTP header and/or Claim).

@TomPallister
Copy link
Member

@iderbyshev nice! doesnt feel like to bad a hack! Ocelot can't do everything :) I will bear this in mind and lets see if we can do something to make this better in the future!

@TomPallister
Copy link
Member

Expected Behavior / New Feature
ReRoute discovery by header value in addition to path.

Actual Behavior / Motivation for New Feature
I am working on versioning problem for API, that is already running in production.
By the current moment version is passed with every request|response via headers.

I would like to configure Ocelot in a such way, so that v1 will be routed to Api 1, v2 to Api 2 an so on.
It also seems that additional constraints on Path could be used in other scenarios.

I can think about following problem with this approach:
should filtering be done by all headers, that are present in ReRoute or should it be one of the list ?

Current implementation
Right now I achieve the behaviour above by adding middleware. But this seems to be a bad solution.
Dear colleagues, is it possible to achieve described behaviour in any other way ?

@pliolis
Copy link

pliolis commented Oct 1, 2018

yes, i need it too in a multi tenant scenario.

@lalocarbone
Copy link

Excellent and amazing work Tom. Need it too for a multi tenant scenario as well !!!!

@Phiph
Copy link

Phiph commented Apr 4, 2019

Yeah header based routing would also be useful for me :) - for a multi tennant scenario

@mrclayman
Copy link

mrclayman commented Jul 12, 2019

Hi all,
I started working on this feature a few days ago since this is something we want to leverage in our deployment of Ocelot.
The idea is that I want to be able to have a set of headers, each with a set of acceptable values, and be able to define whether a request should contain any or all of the defined headers to be accepted for routing to that downroute. Essentially, to say something like this in the configuration file in a downroute section:

"UpstreamHeaderRoutingOptions": {
  "Headers": {
    "Header1": [ "AllowedValue1", "AllowedValue2" ],
    "Header2": [ "AllowedValue1", "AllowedValue2" ]
  },
  "CombinationMode": "<one of Any/All>"
}

Any value from the set of values associated with a single header is currently sufficient for the header to match.
Currently, my aim is to support choosing of a downroute based on a pre-configured set of headers, because that is the itch I need to scratch. I am thinking about making it possible to use placeholders in downstream path templates, but I am a little time-constrained and I have not yet researched how to achieve that.
@TomPallister or anybody from the maintainer team, can I get in touch somehow? I may have a question or two. 😊

Thanks!

@jrsanbornjr
Copy link

I'm looking for this functionality also. Described spot on earlier " v1 will be routed to Api 1, v2 to Api 2". Any recent news on how this effort is going?? Thanks

@mrclayman
Copy link

Hi @jrsanbornjr , I created a pull request #964 a few months ago for the guys to have a look at what I had done. I haven't received any feedback so I do not know if they had a look at it in the meantime.

However, I have gotten to the point where I would prefer being able to route based on claim values so I will likely look into that. Therefore, it might be preferable if my pull request was not actually merged before I get this done.

I am also strongly considering adding support for placeholders as per one of the suggestions above, so that one could specify something like this in the configuration

"UpstreamRoutingHeaders": {
  "Headers": {
    "MyHeader": [ "HeaderValue" ]
  }
},
"DownstreamPathTemplate" : "https://service/{header:MyHeader}/path"

which seems pretty nifty to me. Similarly, claims might have something like http://service/{claim:ClaimName}/path. I have not got around to this yet, though. 🙁

@sachinjagdale
Copy link

I am also waiting for this functionality to support my legacy v1 api and v2 microservice. Will need to have unique gateway route which only be differentiated on header.

@jrsanbornjr
Copy link

jrsanbornjr commented Sep 25, 2019 via email

@sachinjagdale
Copy link

sachinjagdale commented Sep 25, 2019

Thanks @jrsanbornjr for suggestion. Seems good option if routes differentiated on claims as well. Either header or claims feature support needed to differentiate v1 or v2 routes . Will also check on how to extend ocelot in my code for forwarding requesr to correct downstream routes based on header/claims. Please suggest if you have done something like that.

@mrclayman
Copy link

mrclayman commented Oct 22, 2019

@sachinjagdale, are you going to try to implement this? I think I will finally be able to find the time over the next several days to look into adding claim-based routing and placeholder support for claims and headers, so that we don't end up messing with the same code.

@SebastianMajewski
Copy link

@mrclayman great job. Are you planning add header value matching? I need it for my scenario.
Something like:
"UpstreamRoutingHeaders": { "Headers": { "MyHeader": [ "value-{everything}" ] } }

@jrsanbornjr
Copy link

jrsanbornjr commented Oct 28, 2019 via email

@mrclayman
Copy link

mrclayman commented Nov 7, 2019

I also apologize for the delay, other more important tasks got in the way. I managed to clear my backlog a little, so hopefully I will finally have more time now.

@SebastianMajewski it was not planned intially, but I will give some time to research how difficult it would be to implement. 🙂

@falcopr
Copy link

falcopr commented May 18, 2020

Is there some update about this feature?
It would be very beneficial to have this feature since depending on http header data the response from the server will change, like authentication.
I saw that there was a closed Pull-Request about this: https://github.com/ThreeMammals/Ocelot/pull/964/files
But somehow the progress got stuck.

@brettwinters
Copy link

brettwinters commented Jun 9, 2020

I also think I need this feature for my multi-tenant application. I'm using different subdomains for my clients and basically I want to use the "origin" header to switch authentication schemes

route uses Finbuckle to configure via http call to a tenant service

{
        "AuthenticationOptions": {
            "AuthenticationProviderKey": "TenantAuthentication",
            "AllowedScopes": []
        }
},

and this route for admins

{
        "UpstreamRoutingHeaders": {
              "Headers": {
                     "Origin": [ "admin.xxx.com" ]
              }
        },
        "AuthenticationOptions": {
            "AuthenticationProviderKey": "AdminAuthentication",
            "AllowedScopes": []
        }
}

Unless anyone has a better suggestion. Plus I can also think of many other uses for header routing...

@jrsanbornjr
Copy link

jrsanbornjr commented Jun 10, 2020 via email

@brettwinters
Copy link

I'm not sure of the implications, but maybe a better option might be a "delegate route strategy" which I think is the idea here #1236 - I'm thinking of the Delegate Strategy as used here

In that way your users could use any type of logic to return a delegate value which then can be used in the routing config to determine the route

.AddOcelot()
    .WithDelegateRouting(ctx => {
        //any kind of logic here to return a string
         ((HttpContext)ctx).Request.Headers.TryGetValue("origin", out StringValues originValue);
         return Task.FromResult(originValue.ToString());
    })

then in routing config:

{
        "DelegateRouting": {
              "Values": {
                     "admin.xxx.com",
                     "another_route"
              }
        },
}

@mrclayman
Copy link

I owe you guys an apology, too. 😞 I was the creator of PR #964 back when we (at the company I work for) thought we would make use of that feature. However, we have since made the decision to go a different route that would not require this change and I just have not had the time to finish this so I had to essentially abandon all work done on the task.

I have been hoping to return to it asap, but it has not worked out that way yet.

@raman-m
Copy link
Member

raman-m commented Jun 30, 2023

@PraveenValavan commented on June 29, 2023

However, this works for the first time, and the following requests don't get changed to the right host but use the host from the first request instead. This is because the LoadBalancingMiddleware kicks in and replaces the host that I have updated.
Can you please guide me on the right track?

Hi Praveen!
It looks like a developer's coding life hack! Nice! 🤣
Cannot suggest anything with your life hack...

I recommend going these two ways:

  • Use my tips I wrote for Brett Graves. In this case you will have some hybrid solution because multi-tenant redirects will be made by service discovery provider or another multi-tenancy service.
  • Use Azure native tools (I see you deploy apps to Azure cloud). See First approach in my tips

Hope it helps!

P.S. Sure, you can wait for final delivery of current feature in a month or two. Now we have a draft solution ( #964 ) from Zbyněk Novotný.

@raman-m
Copy link
Member

raman-m commented Jun 30, 2023

@mrclayman commented on Jun 29, 2023

I don't even work for the company where we needed that.

No worries! Every company in the world could use Ocelot to build API gateways, including your current one. 😃

Regarding #964 ... It is based on very old, outdated feature branch!
I've created this PR 1 to update your fork repo. Please merge the PR!
After that create new feature branch from develop, apply all changes and create new PR please!

Hope you will start development soon!

@raman-m
Copy link
Member

raman-m commented Jul 17, 2023

@mrclayman We have #1312 by @jlukawska
@jlukawska We have #964 by @mrclayman

Who will win? ⭐ 😋
Your bets, please! ♠️ ♥️ ♣️ ♦️

@mrclayman
Copy link

mrclayman commented Jul 19, 2023

I bet on her. 😄

I've got the changes I made in the past incorporated into the new state of the code base. I could create a new preliminary PR just so ya'll can see, but I have not tested them (unit tests do pass, though).

I would also like to allow the use of placeholders, but I will have to research how to do that first.

mrclayman added a commit to mrclayman/Ocelot that referenced this issue Jul 19, 2023
@mrclayman
Copy link

mrclayman commented Jul 19, 2023

Hm, for some reason, many of the changed files are coming up as "rewritten" even though I really only just added the bits I had implemented in the old branch, dammit.

Let me know if that is a problem. 😕 Right now I feel like redoing everything. Those diffs are pretty much useless.

@raman-m
Copy link
Member

raman-m commented Jul 20, 2023

@mrclayman
Let's move discussion to your PR #964 until the moment you'll create a new PR, maybe...

You are unassigned from this ticket because your solution is draft and it is based on very old source code and feature branch which is 247 commits behind ThreeMammals:develop!
We cannot go with your draft solution. Sorry!

This issue will be assigned to @jlukawska because of ready and solid PR #1312. The merge conflicts will be resolved soon.

@mrclayman
Copy link

Well, if you have another solution that is close to being merged, then it makes little sense to me to keep working on this any longer. I am perfectly fine with somebody else picking up the mantle and I don't really want this to become a race. 🙂

raman-m pushed a commit to mrclayman/Ocelot that referenced this issue Jul 27, 2023
@raman-m raman-m removed help wanted Not actively being worked on. If you plan to contribute, please drop a note. large effort Likely over a week of development effort labels Jul 31, 2023
@raman-m raman-m added the 2023 Annual 2023 release label Nov 29, 2023
@raman-m raman-m added this to the December'23 milestone Nov 29, 2023
@raman-m
Copy link
Member

raman-m commented Nov 29, 2023

Possible delivery in December...

@raman-m raman-m self-assigned this Apr 2, 2024
@raman-m raman-m added the Routing Ocelot feature: Routing label Apr 5, 2024
raman-m added a commit that referenced this issue Apr 18, 2024
* routing based on headers (all specified headers must match)

* routing based on headers for aggregated routes

* unit tests and small modifications

* find placeholders in header templates

* match upstream headers to header templates

* find placeholders name and values, fix regex for finding placeholders values

* fix unit tests

* change header placeholder pattern

* unit tests

* unit tests

* unit tests

* unit tests

* extend validation with checking upstreamheadertemplates, acceptance tests for cases from the issue

* update docs and minor changes

* SA1649 File name should match first type name

* Fix compilation errors by code review after resolving conflicts

* Fix warnings

* File-scoped namespaces

* File-scoped namespace

* Target-typed 'new' expressions (C# 9).
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-9.0/target-typed-new

* IDE1006 Naming rule violation: These words must begin with upper case characters: should_*

* Target-typed 'new' expressions (C# 9).
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-9.0/target-typed-new

* Fix build errors

* DownstreamRouteBuilder

* AggregatesCreator

* IUpstreamHeaderTemplatePatternCreator, RoutesCreator

* UpstreamHeaderTemplatePatternCreator

* FileAggregateRoute

* FileAggregateRoute

* FileRoute

* Route, IRoute

* FileConfigurationFluentValidator

* OcelotBuilder

* DownstreamRouteCreator

* DownstreamRouteFinder

* HeaderMatcher

* DownstreamRouteFinderMiddleware

* UpstreamHeaderTemplate

* Routing folder

* RoutingBasedOnHeadersTests

* Refactor acceptance tests

* AAA pattern in unit tests

* CS8936: Feature 'collection expressions' is not available in C# 10.0.
Please use language version 12.0 or greater.

* Code review by @RaynaldM

* Convert facts to one `Theory`

* AAA pattern

* Add traits

* Update routing.rst

Check grammar and style

* Update docs

---------

Co-authored-by: raman-m <[email protected]>
@raman-m raman-m added merged Issue has been merged to dev and is waiting for the next release and removed accepted Bug or feature would be accepted as a PR or is being worked on labels Apr 18, 2024
@raman-m
Copy link
Member

raman-m commented Apr 18, 2024

Implemented by #1312
Will be released as a part of version 24.0...

@raman-m raman-m closed this as completed Apr 18, 2024
ggnaegi pushed a commit that referenced this issue May 3, 2024
* routing based on headers (all specified headers must match)

* routing based on headers for aggregated routes

* unit tests and small modifications

* find placeholders in header templates

* match upstream headers to header templates

* find placeholders name and values, fix regex for finding placeholders values

* fix unit tests

* change header placeholder pattern

* unit tests

* unit tests

* unit tests

* unit tests

* extend validation with checking upstreamheadertemplates, acceptance tests for cases from the issue

* update docs and minor changes

* SA1649 File name should match first type name

* Fix compilation errors by code review after resolving conflicts

* Fix warnings

* File-scoped namespaces

* File-scoped namespace

* Target-typed 'new' expressions (C# 9).
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-9.0/target-typed-new

* IDE1006 Naming rule violation: These words must begin with upper case characters: should_*

* Target-typed 'new' expressions (C# 9).
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-9.0/target-typed-new

* Fix build errors

* DownstreamRouteBuilder

* AggregatesCreator

* IUpstreamHeaderTemplatePatternCreator, RoutesCreator

* UpstreamHeaderTemplatePatternCreator

* FileAggregateRoute

* FileAggregateRoute

* FileRoute

* Route, IRoute

* FileConfigurationFluentValidator

* OcelotBuilder

* DownstreamRouteCreator

* DownstreamRouteFinder

* HeaderMatcher

* DownstreamRouteFinderMiddleware

* UpstreamHeaderTemplate

* Routing folder

* RoutingBasedOnHeadersTests

* Refactor acceptance tests

* AAA pattern in unit tests

* CS8936: Feature 'collection expressions' is not available in C# 10.0.
Please use language version 12.0 or greater.

* Code review by @RaynaldM

* Convert facts to one `Theory`

* AAA pattern

* Add traits

* Update routing.rst

Check grammar and style

* Update docs

---------

Co-authored-by: raman-m <[email protected]>
@raman-m raman-m added Spring'24 Spring 2024 release and removed 2023 Annual 2023 release labels May 22, 2024
@raman-m raman-m modified the milestones: Annual 2023, Spring'24 May 22, 2024
raman-m added a commit that referenced this issue May 23, 2024
* routing based on headers (all specified headers must match)

* routing based on headers for aggregated routes

* unit tests and small modifications

* find placeholders in header templates

* match upstream headers to header templates

* find placeholders name and values, fix regex for finding placeholders values

* fix unit tests

* change header placeholder pattern

* unit tests

* unit tests

* unit tests

* unit tests

* extend validation with checking upstreamheadertemplates, acceptance tests for cases from the issue

* update docs and minor changes

* SA1649 File name should match first type name

* Fix compilation errors by code review after resolving conflicts

* Fix warnings

* File-scoped namespaces

* File-scoped namespace

* Target-typed 'new' expressions (C# 9).
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-9.0/target-typed-new

* IDE1006 Naming rule violation: These words must begin with upper case characters: should_*

* Target-typed 'new' expressions (C# 9).
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-9.0/target-typed-new

* Fix build errors

* DownstreamRouteBuilder

* AggregatesCreator

* IUpstreamHeaderTemplatePatternCreator, RoutesCreator

* UpstreamHeaderTemplatePatternCreator

* FileAggregateRoute

* FileAggregateRoute

* FileRoute

* Route, IRoute

* FileConfigurationFluentValidator

* OcelotBuilder

* DownstreamRouteCreator

* DownstreamRouteFinder

* HeaderMatcher

* DownstreamRouteFinderMiddleware

* UpstreamHeaderTemplate

* Routing folder

* RoutingBasedOnHeadersTests

* Refactor acceptance tests

* AAA pattern in unit tests

* CS8936: Feature 'collection expressions' is not available in C# 10.0.
Please use language version 12.0 or greater.

* Code review by @RaynaldM

* Convert facts to one `Theory`

* AAA pattern

* Add traits

* Update routing.rst

Check grammar and style

* Update docs

---------

Co-authored-by: raman-m <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature A new feature merged Issue has been merged to dev and is waiting for the next release Routing Ocelot feature: Routing Spring'24 Spring 2024 release
Projects
None yet