Add customizable tenant header support for HTTP routing

Introduced `MultitenancyHttpOptions` to configure the tenant header name used for HTTP routing. Updated `HeaderTenantResolver` to utilize this option and modified the server setup to allow overriding the default header name. This enables more flexible multitenancy configurations.
This commit is contained in:
Sipke Schoorstra 2025-02-09 08:24:55 +01:00
parent 484a126693
commit d83bb3b834
4 changed files with 40 additions and 3 deletions

View file

@ -659,7 +659,11 @@ services
}
});
elsa.UseTenantHttpRouting();
elsa.UseTenantHttpRouting(tenantHttpRouting =>
{
// Override the tenant header name with a custom one.
tenantHttpRouting.WithTenantHeader("X-Company-Id");
});
}
elsa.InstallDropIns(options => options.DropInRootDirectory = Path.Combine(Directory.GetCurrentDirectory(), "App_Data", "DropIns"));

View file

@ -3,6 +3,7 @@ using Elsa.Features.Abstractions;
using Elsa.Features.Attributes;
using Elsa.Features.Services;
using Elsa.Http.Features;
using Elsa.Tenants.AspNetCore.Options;
using Elsa.Tenants.AspNetCore.Services;
using Elsa.Tenants.Features;
using Microsoft.Extensions.DependencyInjection;
@ -13,6 +14,26 @@ namespace Elsa.Tenants.AspNetCore.Features;
[DependencyOf(typeof(TenantsFeature))]
public class MultitenantHttpRoutingFeature(IModule module) : FeatureBase(module)
{
private Action<MultitenancyHttpOptions> _configureMultitenancyHttpOptions = _ => { };
/// <summary>
/// Configures the MultitenantHttpRoutingFeature to use a specific tenant header name.
/// </summary>
/// <param name="headerName">The name of the HTTP header used to identify the tenant.</param>
/// <returns>The current instance of <see cref="MultitenantHttpRoutingFeature"/> for fluent configuration.</returns>
public MultitenantHttpRoutingFeature WithTenantHeader(string headerName) => WithMultitenancyHttpOptions(options => options.TenantHeaderName = headerName);
/// <summary>
/// Configures the MultitenantHttpRoutingFeature with custom multitenancy HTTP options.
/// </summary>
/// <param name="configure">The action to configure <see cref="MultitenancyHttpOptions"/>.</param>
/// <returns>The current instance of <see cref="MultitenantHttpRoutingFeature"/> for fluent configuration.</returns>
public MultitenantHttpRoutingFeature WithMultitenancyHttpOptions(Action<MultitenancyHttpOptions> configure)
{
_configureMultitenancyHttpOptions = configure;
return this;
}
public override void Configure()
{
Module.Configure<HttpFeature>(feature =>
@ -24,6 +45,9 @@ public class MultitenantHttpRoutingFeature(IModule module) : FeatureBase(module)
public override void Apply()
{
// Multitenancy HTTP options.
Services.Configure(_configureMultitenancyHttpOptions);
// Tenant resolvers.
Services
.AddScoped<ITenantResolver, RoutePrefixTenantResolver>()

View file

@ -0,0 +1,6 @@
namespace Elsa.Tenants.AspNetCore.Options;
public class MultitenancyHttpOptions
{
public string TenantHeaderName { get; set; } = "X-Tenant-Id";
}

View file

@ -1,12 +1,14 @@
using Elsa.Common.Multitenancy;
using Elsa.Tenants.AspNetCore.Options;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
namespace Elsa.Tenants.AspNetCore;
/// <summary>
/// Resolves the tenant based on the header in the request.
/// </summary>
public class HeaderTenantResolver(IHttpContextAccessor httpContextAccessor) : TenantResolverBase
public class HeaderTenantResolver(IHttpContextAccessor httpContextAccessor, IOptions<MultitenancyHttpOptions> options) : TenantResolverBase
{
protected override TenantResolverResult Resolve(TenantResolverContext context)
{
@ -15,7 +17,8 @@ public class HeaderTenantResolver(IHttpContextAccessor httpContextAccessor) : Te
if (httpContext == null)
return Unresolved();
var tenantId = httpContext.Request.Headers["X-Tenant-Id"].FirstOrDefault();
var headerName = options.Value.TenantHeaderName;
var tenantId = httpContext.Request.Headers[headerName].FirstOrDefault();
return AutoResolve(tenantId);
}