8000 Allow service discovery providers to specify a path prefix by dauthleikr · Pull Request #2289 · ThreeMammals/Ocelot · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Allow service discovery providers to specify a path prefix #2289

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

Conversation

dauthleikr
Copy link
@dauthleikr dauthleikr commented Apr 23, 2025

New Feature

This allows service discovery provider implementations to specify a path prefix.

Proposed changes

We need this for cases where services run behind a reverse proxy and are exposed like https://reverse.proxy/someService/endpoints. someService would be the "path prefix" in this case.

@coveralls
Copy link
Collaborator

Coverage Status

coverage: 85.752% (+0.002%) from 85.75%
when pulling d3c28d7 on dauthleikr:service-discovery-path-prefix
into e78fe42 on ThreeMammals:develop.

@raman-m raman-m requested review from RaynaldM, raman-m and ggnaegi April 24, 2025 09:23
@raman-m raman-m added Service Discovery Ocelot feature: Service Discovery Routing Ocelot feature: Routing proposal Proposal for a new functionality in Ocelot labels Apr 24, 2025
@raman-m
Copy link
Member
raman-m commented Apr 24, 2025
10000

Hello, @dauthleikr
Thank you for your interest in Ocelot, however...

New Feature

This allows service discovery provider implementations to specify a path prefix.

Which service discovery provider are you referring to? Consul, Kubernetes, Eureka, or Service Fabric?

As far as I am aware, these providers do not require any custom service prefixes, with the possible exception of Service Fabric. Therefore, path prefixes can be conveniently specified when defining a route path template. For instance:

{
  "ServiceName": "someService",
  "UpstreamPathTemplate": "/someService/endpoints.someService",
  "DownstreamPathTemplate": "/endpoints",
  "LoadBalancerOptions": {
    "Type": "LeastConnection"
  }
}

To clarify, I am unable to comprehend your template endpoints.someService. Could you provide your ocelot.json file? Additionally, please share practical examples illustrating upstreamservice discoverydownstream routing.

Copy link
Member
@raman-m raman-m left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR doesn't look professional!
Perhaps I don't fully understand the purpose of this pull request or the specific issue it aims to address for the discovery provider.
And, what's your discovery provider?

Have you reviewed the documentation detailing how to develop a custom service discovery provider?

P.S. According to our development process, each pull request must be linked to a corresponding issue or discussion. However, this pull request lacks any related issues and has not been discussed.

@@ -16,7 +16,7 @@ public DownstreamRequest(HttpRequestMessage request)
Scheme = _request.RequestUri.Scheme;
Host = _request.RequestUri.Host;
Port = _request.RequestUri.Port;
AbsolutePath = _request.RequestUri.AbsolutePath;
Path = _request.RequestUri.AbsolutePath;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You rename the property, yet the underlying logic remains. What is the purpose of such a renaming?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because AbsolutePath is now a relative path, if the prefix exists (the naming would be misleading).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Misleading for whom? I don't think the 'Absolute' prefix is misleading to anyone, as it is an internal class of Ocelot Core. 😉

public string AbsolutePath { get; set; }
public string Path { get; set; }

public string ServicePathPrefix { get; set; } = string.Empty;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To address the issue effectively, you have to share your solution by uploading it to GitHub. Introducing new property should be justified by a compelling reason, particularly when the existing implementation fails to resolve t 10000 he specific use case.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cannot upload our custom service discovery implementation to github. Basically our service discovery returns an Uri, which is passed to ocelot as follows:

private static List<Service> AsServiceList(ImmutableArray<ServiceInstance> serviceInstances)
{
    return serviceInstances.Select(item =>
    {
        var uri = new Uri(item.Uri, UriKind.Absolute);
        return new Service(item.ServiceName, 
            new ServiceHostAndPort(uri.Host, uri.Port, uri.Scheme, /* This is the new "prefix" */ uri.AbsolutePath), 
            item.Id, 
            "v1", 
            []);
    }).ToList();
}

{
var uriBuilder = new UriBuilder
{
Port = Port,
Host = Host,
Path = AbsolutePath,
Path = string.IsNullOrEmpty(ServicePathPrefix) ? Path : $"{ServicePathPrefix.TrimEnd('/')}/{Path.TrimStart('/')}",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trimming slashes is not advisable for Ocelot's current functionality for routing features and bugfixes which are related to slash in the path: last slash, omitting slash, Catch All paths.
We recommend verifying this by creating acceptance with and enabling a real service discovery provider.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TrimStart merely fixes the case where the Path already starts with a slash - without the TrimStart, we could have two slashes (a hardcoded slash is part of the interpolated string).

public string DownstreamHost { get; }
public int DownstreamPort { get; }
public string Scheme { get; }
public string PathPrefix { get; } = string.Empty;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you reviewed the name of the class? 😆
This class should strictly contain information relevant to host construction: scheme, address, and port. Nothing else!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree the naming of the class no longer fits. Do you have a recommendation on how to proceed? Shall I rename the class?

.Build();
var serviceProviderConfig = new ServiceProviderConfigurationBuilder()
.Build();
GivenTheDownStreamUrlIs("http://my.url/prefix/abc?q=123");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good unit test, but write more acceptance tests please

@dauthleikr
Copy link
Author

Hi @raman-m, thanks for responding.

This is only useful when using a custom service discovery implementation (our case).
Here is an example route:

  {
    "UpstreamPathTemplate": "/cashpoint/v1/{everything}",
    "DownstreamPathTemplate": "/api/v1/Cashpoint/{everything}",
    "DownstreamScheme": "https",
    "ServiceName": "PaymentApi",
    "UpstreamHttpMethod": [],
    "SwaggerKey": "payment.api"
  },

Our software is deployed on customer infrastructure, so "PaymentApi" may run on a dedicated host/container, in which case this change is not needed (simply return host and port through our service discovery). However in some cases, customers may choose to expose "PaymentApi" through a reverse proxy, so the base path of the API is, for example, https:\\somesoftware.customer.com\PaymentApi\.

Now, you might say that we should simply adjust the route in ocelot.json to the following:

  {
    "UpstreamPathTemplate": "/cashpoint/v1/{everything}",
    "DownstreamPathTemplate": "/PaymentApi/api/v1/Cashpoint/{everything}",
    "DownstreamScheme": "https",
    "ServiceName": "PaymentApi",
    "UpstreamHttpMethod": [],
    "SwaggerKey": "payment.api"
  },

(notice the change in DownstreamPathTemplate). However that is not suitable for us, because

  1. We deploy to hundreds of customer infrastructures, meaning we would need hundreds of ocelot.json variants (hard to manage)
  2. We might load balance between multiple "PaymentApi"s, some of which run behind a reverse proxy, https:\\somesoftware.customer.com\PaymentApi\, and some of which are containers which can be directly reached (just IP and port, no base path of "\PaymentApi"). So the DownstreamPathTemplate would have to differ depending on which instance of "PaymentApi" is resolved.

@raman-m
Copy link
Member
raman-m commented Apr 24, 2025

Wow! The path prefix has magically disappeared, and now you're telling me about host variations. If the customer's hosts vary, then you must use a service discovery tool to maintain a registry of those hosts.

https:\\somesoftware.customer.com\PaymentApi\
I am just curious, why do you use backslash char here? It is invalid URL 😉

This is only useful when using a custom service discovery implementation (our case).

Please proceed to develop a custom provider, your own one. In this custom provider you can do whatever you want with hosts and paths including prefixes.
Good luck!

We are ready to assist you and consult you.

@raman-m
Copy link
Member
raman-m commented Apr 24, 2025

I've decided to close this pull request because it is entirely unnecessary for the Ocelot team. It adds no new functionality, and the user scenario can be handled by any service discovery software.

P.S. Because it was a pull request, I can't move it to discussions: only issues can be moved to discussions and vice versa.

@raman-m raman-m closed this Apr 24, 2025
@raman-m raman-m added wontfix No plan to include this issue in the Ocelot core functionality. and removed proposal Proposal for a new functionality in Ocelot labels Apr 24, 2025
@dauthleikr
Copy link
Author

@raman-m I am confused on how to connect our service discovery (which resolved to Uri's) to ocelot, as we cannot use the path. I opened this PR hoping you would see it as a minor improvement that helps others which also implement some Uri-based custom service discovery. I was, of course, ready to polish/rework this prototye-PR myself. Though if your insist it is useless to the ocelot team, I can accept that. I guess we have to maintain a private fork :/

@raman-m
Copy link
Member
raman-m commented Apr 24, 2025

I am confused on how to connect our service discovery (which resolved to Uri's) to ocelot, as we cannot use the path.

Once again! Develop your own custom provider.


I guess we have to maintain a private fork :/

Good luck! Ocelot is open-source software, so you can copy the code into a private repository. However, in that case, don't expect any consulting or support from us.


helps others which also implement some Uri-based custom service discovery.

If you want to help other, open a discussion first! Seems in appropriate Ideas category please

@ThreeMammals ThreeMammals deleted a comment from dauthleikr Apr 24, 2025
@ThreeMammals ThreeMammals locked and limited conversation to collaborators Apr 24, 2025
@raman-m raman-m removed request for RaynaldM and ggnaegi April 28, 2025 10:32
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Routing Ocelot feature: Routing Service Discovery Ocelot feature: Service Discovery wontfix No plan to include this issue in the Ocelot core functionality.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants
0