I have an ocelot gateway that is routing API requests to multiple microservices that are supporting multitenancy.
However, I wanted to use the azure storage APIs to simplify the solution instead of writing code to handle the blobs. I have one storage account for each Tenant, and the issue is I want to switch to the correct storage account based on the tenant-id I have in request as a parameter.
I tried to rewrite the url and switch the downstream host using a middleware.
in program.cs
var configuration = new OcelotPipelineConfiguration
{
PreQueryStringBuilderMiddleware = async (ctx, next) =>
{
await preQueryStringBuilderMiddleware.InvokeAsync(ctx, options, next);
}
};
Inside the middleware
{
//getting the tenantid from the request url parameter
......
var downstreamRoute = httpContext.Items.DownstreamRoute();
List<DownstreamHostAndPort> downstreamHostAndPorts = new List<DownstreamHostAndPort>()
{
//setting the tenantid as the host
new DownstreamHostAndPort($"{tenantId}{storageAccountOptions.StorageAccountSuffix}",storageAccountOptions.StorageAccountDefaultPort)
};
httpContext.Items.UpsertDownstreamRoute(
new DownstreamRoute(
key: downstreamRoute.Key,
upstreamPathTemplate: downstreamRoute.UpstreamPathTemplate,
upstreamHeadersFindAndReplace: downstreamRoute.UpstreamHeadersFindAndReplace,
downstreamHeadersFindAndReplace: downstreamRoute.DownstreamHeadersFindAndReplace,
downstreamAddresses: downstreamHostAndPorts,
serviceName: downstreamRoute.ServiceName,
serviceNamespace: downstreamRoute.ServiceNamespace,
httpHandlerOptions: downstreamRoute.HttpHandlerOptions,
useServiceDiscovery: downstreamRoute.UseServiceDiscovery,
enableEndpointEndpointRateLimiting: downstreamRoute.EnableEndpointEndpointRateLimiting,
qosOptions: downstreamRoute.QosOptions,
downstreamScheme: downstreamRoute.DownstreamScheme,
requestIdKey: downstreamRoute.RequestIdKey,
isCached: downstreamRoute.IsCached,
cacheOptions: downstreamRoute.CacheOptions,
loadBalancerOptions: downstreamRoute.LoadBalancerOptions,
rateLimitOptions: downstreamRoute.RateLimitOptions,
routeClaimsRequirement: downstreamRoute.RouteClaimsRequirement,
claimsToQueries: downstreamRoute.ClaimsToQueries,
claimsToHeaders: downstreamRoute.ClaimsToHeaders,
claimsToClaims: downstreamRoute.ClaimsToClaims,
claimsToPath: downstreamRoute.ClaimsToPath,
isAuthenticated: downstreamRoute.IsAuthenticated,
isAuthorized: downstreamRoute.IsAuthorized,
authenticationOptions: downstreamRoute.AuthenticationOptions,
downstreamPathTemplate: downstreamRoute.DownstreamPathTemplate,
loadBalancerKey: downstreamRoute.LoadBalancerKey,
delegatingHandlers: downstreamRoute.DelegatingHandlers,
addHeadersToDownstream: downstreamRoute.AddHeadersToDownstream,
addHeadersToUpstream: downstreamRoute.AddHeadersToUpstream,
dangerousAcceptAnyServerCertificateValidator: downstreamRoute.DangerousAcceptAnyServerCertificateValidator,
securityOptions: downstreamRoute.SecurityOptions,
downstreamHttpMethod: downstreamRoute.DownstreamHttpMethod,
downstreamHttpVersion: downstreamRoute.DownstreamHttpVersion
));
}
await next.Invoke();
}
The config file looks like below
{
"DownstreamPathTemplate": "/{sasToken}",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "ReplacedInRuntime",
"Port": 443
}
],
"UpstreamPathTemplate": "/api/{version}/Media/{tenantId}/{sasToken}",
"UpstreamHttpMethod": [ "POST", "PUT", "GET", "DELETE" ],
"SwaggerKey": "backgroundService"
},
This works the first time and not afterwards.
I can't use dynamic routing as I have other routes in my config.
Thanks in advance.
I figured this could be solved using Delegte Handlers
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request.RequestUri != null && request.RequestUri.ToString().Contains("the path to override"))
{
//do your thing
}
// then forward the request
return await base.SendAsync(request, cancellationToken);
}