2

I have an API Gateway running in .NET Core 3.1 using Ocelot. Everything works fine as expected.

Now I'm trying to substitute the downstream host during the middleware process.

Here's my configuration.json:

{
  "Routes": [
    {
      "DownstreamPathTemplate": "/api/product/getProduct",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": "44300"
        }
      ],
      "UpstreamPathTemplate": "/getProduct"
    }
  ],
  "GlobalConfiguration": {
    "BaseUrl": "https://localhost:62012/"
  }
}

I want to change the Host in the DownstreamHostAndPorts at runtime as by this time from grabbing the claims out of the jwt I will know the tenant the user belongs to therefore know where to route there request.

To be more clear, a request to the gateway comes in at http://localhost:62012/api/getProduct,

Then I get the tenant from the jwt in the request that made this call and then redirect the request to the relevant api like so

http://tenant1.com/api/product/getProduct or http://tenant2.com/api/product/getProduct

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
KTOV
  • 559
  • 3
  • 14
  • 39

1 Answers1

3

You need to create custom middleware and inject it after Authentication middleware. The best extension point would be PreAuthorisationMiddleware. Assuming you have a service to resolve tenant uri by user claims, something like this:

public interface ITenantHostResolver
{
    Task<Uri> Resolve(ClaimsPrincipal claimsPrincipal);
}

In your Startup class inject the middleware that will override downstream settings:

public void Configure(IApplicationBuilder app)
{

    var conf = new OcelotPipelineConfiguration
    {
        PreAuthorizationMiddleware = async (httpContext, next) =>
        {
            if(!httpContext.Request.Path.Equals("/api/product/getProduct"))
            {
                await next.Invoke();

                return;
            }

            var claimsPrincipal = httpContext.User;

            var tenantHostResolver = httpContext.RequestServices.GetRequiredService<ITenantHostResolver>();

            var tenantHostAndPort = await tenantHostResolver.Resolve(claimsPrincipal);

            var downstreamRequest = httpContext.Items.DownstreamRequest();

            downstreamRequest.Host = tenantHostAndPort.Host;

            downstreamRequest.Port = tenantHostAndPort.Port;

            downstreamRequest.Scheme = tenantHostAndPort.Scheme;

            await next.Invoke();
        }
    };

    app.UseCustomOcelot(conf).Wait();
}
kara
  • 3,205
  • 4
  • 20
  • 34
Artur
  • 4,595
  • 25
  • 38
  • Is there a way to check if the transformation of the downstreamRequest has worked because I always get the message that my call did not succeed, but the request Uri printed in the log is the one that should be transformed. – Dimitris Jan 31 '22 at 10:34
  • I went through the Ocelot code and this can never work. There is a call "UseLoadBalancingMiddleware" which happens after any of your custom middleware you can provide for Ocelot and it resets the changes made to port and host. – Dimitris Feb 01 '22 at 12:35