Files
tiku-backend.net/Tiku.Api/Middleware/TenantResolutionMiddleware.cs
xiong c497a3ca8d
Some checks failed
ci / release-gate (push) Has been cancelled
清理代码
2026-08-03 12:31:39 +08:00

70 lines
2.8 KiB
C#

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Tiku.Api.Options;
using Tiku.Application.Security;
using Tiku.Application.Tenancy;
namespace Tiku.Api.Middleware;
public sealed class TenantResolutionMiddleware(RequestDelegate next)
{
public async Task InvokeAsync(
HttpContext context,
ITenantDirectory tenantDirectory,
ITenantContextInitializer tenantInitializer,
IOptions<TenantResolutionOptions> options)
{
var host = NormalizeHost(context.Request.Host.Host);
var isPlatformHost = options.Value.PlatformHosts.Any(candidate =>
string.Equals(NormalizeHost(candidate), host, StringComparison.OrdinalIgnoreCase));
TenantDirectoryEntry? tenant = null;
if (!isPlatformHost && host is not null)
{
tenant = await tenantDirectory.FindByHostAsync(host, context.RequestAborted);
if (tenant is null && !IsExemptPath(context.Request.Path, options.Value.ExemptPathPrefixes))
{
context.Response.StatusCode = StatusCodes.Status404NotFound;
await context.Response.WriteAsJsonAsync(new ProblemDetails
{
Title = "Tenant was not found.",
Status = StatusCodes.Status404NotFound,
Detail = "The request host is not assigned to an active tenant."
}, context.RequestAborted);
return;
}
}
else if (isPlatformHost && IsAllowedTenantCodePath(
context.Request.Path,
options.Value.TenantCodePathPrefixes))
{
var tenantCode = context.Request.Headers["x-tenant-code"].FirstOrDefault()
?? context.Request.Query["tenantCode"].FirstOrDefault();
if (!string.IsNullOrWhiteSpace(tenantCode))
tenant = await tenantDirectory.FindByCodeAsync(tenantCode.Trim(), context.RequestAborted);
}
if (tenant is not null)
tenantInitializer.Initialize(
tenant.TenantId,
tenant.TenantCode,
tenant.Host is null ? TenantResolutionSource.TenantCode : TenantResolutionSource.Host);
await next(context);
}
private static string? NormalizeHost(string? host)
{
return string.IsNullOrWhiteSpace(host) ? null : host.Trim().TrimEnd('.').ToLowerInvariant();
}
private static bool IsExemptPath(PathString path, IEnumerable<string> prefixes)
{
return prefixes.Any(prefix => path.StartsWithSegments(prefix, StringComparison.OrdinalIgnoreCase));
}
private static bool IsAllowedTenantCodePath(PathString path, IEnumerable<string> prefixes)
{
return prefixes.Any(prefix => path.StartsWithSegments(prefix, StringComparison.OrdinalIgnoreCase));
}
}