Scenario: Header + Route Resolution with ASP.NET Core Enforcement¶
Goal¶
Document a sample that shows deterministic tenant resolution from route first, host second, and header fallback, with ProblemDetails responses when a tenant is missing or inactive
Sample name and location¶
- Solution:
CleanArchitecture.Extensions.Samples.Multitenancy.HeaderAndRouteResolution - Path:
samples/CleanArchitecture.Extensions.Samples.Multitenancy.HeaderAndRouteResolution
Modules used¶
- Multitenancy core (resolution pipeline + behaviors)
- Multitenancy.AspNetCore (middleware, attributes, ProblemDetails)
Prerequisites¶
- Install the .NET SDK required by the Clean Architecture template.
- Keep numbered step comments in code changes and mirror them in the sample README per repository guidance.
Steps¶
- Install Jason Taylor's Clean Architecture template and create the base Web API-only solution (no extensions yet).
- Install or update the template to the version we align with:
- From the repo root, create the sample solution under
CleanArchitecture.Extensions/samplesusing SQLite: - Verify the output folder exists and contains the new solution file plus
src/andtests/. - Reference the multitenancy projects directly from the repository while iterating locally.
- When you switch back to NuGet, replace these
<ProjectReference />entries with<PackageReference />entries pointing to the latest published versions. samples/CleanArchitecture.Extensions.Samples.Multitenancy.HeaderAndRouteResolution/src/Application/Application.csproj:samples/CleanArchitecture.Extensions.Samples.Multitenancy.HeaderAndRouteResolution/src/Web/Web.csproj:- Configure
MultitenancyOptionsfor route-first ordering (Route > Host > Header > Query > Claim), set header nameX-Tenant-ID, require tenants by default, allow explicitly anonymous endpoints, and disable fallback tenants. samples/CleanArchitecture.Extensions.Samples.Multitenancy.HeaderAndRouteResolution/src/Web/DependencyInjection.cs:// Step 3: (Begin) Multitenancy configuration imports using CleanArchitecture.Extensions.Multitenancy; using CleanArchitecture.Extensions.Multitenancy.Configuration; // Step 3: (End) Multitenancy configuration imports// Step 3: (Begin) Configure multitenancy resolution defaults builder.Services.Configure<MultitenancyOptions>(options => { options.RequireTenantByDefault = true; options.AllowAnonymous = true; options.HeaderNames = new[] { "X-Tenant-ID" }; options.ResolutionOrder = new List<TenantResolutionSource> { TenantResolutionSource.Route, TenantResolutionSource.Host, TenantResolutionSource.Header, TenantResolutionSource.QueryString, TenantResolutionSource.Claim }; options.FallbackTenant = null; options.FallbackTenantId = null; }); // Step 3: (End) Configure multitenancy resolution defaults- Register services with
AddCleanArchitectureMultitenancythenAddCleanArchitectureMultitenancyAspNetCore(autoUseMiddleware: false); addUseCleanArchitectureMultitenancyafter routing and before authentication/authorization. samples/CleanArchitecture.Extensions.Samples.Multitenancy.HeaderAndRouteResolution/src/Web/DependencyInjection.cs:samples/CleanArchitecture.Extensions.Samples.Multitenancy.HeaderAndRouteResolution/src/Web/Program.cs:-
Add route conventions that group tenant-bound APIs under
/tenants/{tenantId}/...; keep health/status endpoints outside the group for anonymous access. -
samples/CleanArchitecture.Extensions.Samples.Multitenancy.HeaderAndRouteResolution/src/Web/Infrastructure/WebApplicationExtensions.cs: -
Decorate tenant-bound endpoints with
RequireTenant, and mark public endpoints withAllowAnonymousTenantto keep resolution optional without enforcement (requiresAllowAnonymous = truein Step 3). samples/CleanArchitecture.Extensions.Samples.Multitenancy.HeaderAndRouteResolution/src/Web/Infrastructure/WebApplicationExtensions.cs:samples/CleanArchitecture.Extensions.Samples.Multitenancy.HeaderAndRouteResolution/src/Web/Program.cs:- Enable
TenantExceptionHandler/ProblemDetails so unresolved tenants return 400, missing tenants return 404, and suspended tenants return 403. samples/CleanArchitecture.Extensions.Samples.Multitenancy.HeaderAndRouteResolution/src/Web/DependencyInjection.cs:samples/CleanArchitecture.Extensions.Samples.Multitenancy.HeaderAndRouteResolution/src/Web/Program.cs:- Add integration tests that cover: resolved via route, resolved via host mapping, header fallback when the route is absent, conflict handling when route/header disagree, and enforcement responses when no tenant is provided.
- Update the sample README with the walkthrough (inputs, expected status codes) and middleware ordering reminders.
Validation¶
- Requests with
/tenants/{tenantId}succeed only when the tenant exists and is active. - Requests without tenant context return the expected ProblemDetails payloads.
- Tenant context is cleared per request (no AsyncLocal leakage between tests).