Search code examples
c#gatewayocelot

Ocelot placeholder for downstream host


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


Solution

  • 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();
    }