Example: ASP.NET Core API hosting pipeline¶
hosting-pipeline.md records how an ASP.NET Core Web API is hosted and how a
request travels through the middleware pipeline before it reaches a controller
action or a minimal API endpoint. It is the page where the SAIF CoreAPIStartup
base class is named for what it is: a source of behavior that the repository does
not show. Doc gen produces one per ASP.NET Core API at
docs/apis/<api-slug>/hosting-pipeline.md.
What reviewers should check¶
- The host model matches
Program.cs. A generic host withUseStartup<T>, a minimal host (WebApplication.CreateBuilder) and a minimal host that drives aStartupinstance by hand are three different shapes, and a repository that mixes them must say so. Never assume aStartup.csexists. - Every base-class flag set in the startup constructor appears in the flags
table, including the ones that are not set.
EnableSecureEnvironmentClient,EnableNewtonsoftJsonSerializer,DisableAuthServicesand theEnvironmentNameoverride each change what the service does. - The middleware table follows source order exactly. Calls contributed by the
base class or a package (
base.Configure,ConfigureMiddlewaredeclared only on the base) are one opaque row each. A pipeline written out asUseRouting->UseAuthentication->UseAuthorization->UseEndpointswithout a source line behind it is invented. - Package behavior carries the dagger marker, and the page carries the canonical dependency-doc note. A base-class contribution written as an unmarked application fact is a finding.
- A commented-out middleware call is a row with Status "Commented out", not an omission. Absent and commented out have different consequences.
- The versioning, OpenAPI, health check and telemetry tables each say what the source shows. "None found" and "Not determined from source" are valid answers; an invented ping route is not.
- The Mermaid diagram uses a standard fence, is drawn in table order, and shows each opaque contribution as a single node.
Example¶
The document below is a complete, correct api-hosting-pipeline.md for a
fictional ASP.NET Core API.
View rendered example — the same document as a real page, so you can compare rendering as well as source.
---
title: Hosting pipeline
summary: Host model, server, ordered middleware pipeline, API versioning, OpenAPI exposure, health checks, and telemetry.
status: draft
owner: Premium Systems Team
last_reviewed: 2026-10-21
---
# Hosting pipeline
`SAIF.Premium.Api` is an ASP.NET Core 8 Web API hosted in-process by IIS through
the ASP.NET Core Module V2. `Program.cs` uses the minimal host
(`WebApplication.CreateBuilder`) but hands service and middleware configuration
to a `Startup` type derived from the SAIF `CoreAPIStartup` base class, so the
pipeline is a mix of calls the repository makes and contributions the base class
makes that source does not show. A request passes the base class's opaque
`ConfigureMiddleware` step, Swagger, and the base class's opaque `Configure`
step before it reaches a controller action or the one minimal API group. API
versioning is read from the URL segment, so `v1` and `v2` are part of the route.
## Host model
| Field | Value |
| --- | --- |
| **Entry point** | `src/SAIF.Premium.Api/Program.cs` |
| **Host builder** | Minimal host - `WebApplication.CreateBuilder(args)` driving a `Startup` instance by hand |
| **Startup call chain** | `new Startup(builder.Configuration)` -> `startup.ConfigureServices(builder.Services)` -> `builder.Build()` -> `startup.ConfigureMiddleware(app)` -> `startup.Configure(app, app.Environment)` -> `app.MapGroup("/v1/RateTables").MapRateTableEndpoints()` -> `app.Run()` |
| **Host-builder extensions** | `builder.Host.ConfigureOpenTelemetry(o => o.Owner = "Premium Systems Team")` |
| **Startup type** | `Startup` (`src/SAIF.Premium.Api/Startup.cs`) |
| **Base class** | `CoreAPIStartup` (`SAIF.NetCore.ServiceExtensions` `8.2.1`) |
| **Target framework** | `net8.0` |
The repository uses both host styles: the host is a minimal host, and
`Program.cs` calls the `Startup` methods itself rather than through
`UseStartup<Startup>()`. `Startup.ConfigureServices` calls
`base.ConfigureServices(services)` as its first statement, before any local
registration. `Startup` declares no `ConfigureMiddleware` method;
`startup.ConfigureMiddleware(app)` resolves to a method that exists only on
`CoreAPIStartup`, so what it registers is not visible in source.
### Base-class flags
| Flag or call | Value | Set in | Effect (as far as source shows) |
| --- | --- | --- | --- |
| `EnableSecureEnvironmentClient<Startup>()` | Called | `src/SAIF.Premium.Api/Startup.cs:22` | Enables the secure `IEnvironmentClient`, which decrypts secure settings; the registration itself is opaque, contributed by `SAIF.NetCore.ServiceExtensions`† |
| `EnableNewtonsoftJsonSerializer` | Not set | - | The base class's default serializer stays in effect† |
| `DisableAuthServices` | Not set | - | The base class's authentication and authorization services stay registered; their configuration is opaque† |
| `WebHostEnvironment.EnvironmentName` | `Configuration["AppSettings:ApplicationHost"]` | `src/SAIF.Premium.Api/Startup.cs:23` | The environment name comes from `appsettings.json`, not from `ASPNETCORE_ENVIRONMENT` |
## Server
| Aspect | Finding |
| --- | --- |
| Server | IIS in-process (ASP.NET Core Module V2) |
| Hosting model evidence | `web.config` `<aspNetCore ... hostingModel="InProcess">` and `<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>` in `src/SAIF.Premium.Api/SAIF.Premium.Api.csproj` |
| `web.config` notes | The checked-in `arguments` value still references `bin\Debug\net6.0\SAIF.Premium.Api.dll` while the target framework is `net8.0`; recorded in [Tech debt](tech-debt.md) |
| Local launch profiles | `Properties\launchSettings.json` - profiles `SAIF.Premium.Api` (`Project`) and `IIS Express`, both with `launchUrl` = `swagger` (local development only) |
## Middleware pipeline
| Order | Middleware | Registered in | Condition | Status | Notes |
| --- | --- | --- | --- | --- | --- |
| 1 | `startup.ConfigureMiddleware(app)` | `src/SAIF.Premium.Api/Program.cs:19` | Always | Opaque | Declared only on `CoreAPIStartup` (`SAIF.NetCore.ServiceExtensions`)† - not visible in source; runs before `Startup.Configure` |
| 2 | `app.UseHttpsRedirection()` | `src/SAIF.Premium.Api/Startup.cs:50` | - | Commented out | Disabled in source, so this service applies no HTTPS redirection of its own |
| 3 | `app.UseSwagger()` | `src/SAIF.Premium.Api/Startup.cs:51` | Always | Active | Not gated by environment; the OpenAPI documents are served on every track |
| 4 | `app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "Premium API v1"); c.SwaggerEndpoint("/swagger/v2/swagger.json", "Premium API v2"); })` | `src/SAIF.Premium.Api/Startup.cs:52` | Always | Active | One endpoint per declared API version |
| 5 | `base.Configure(app, env)` | `src/SAIF.Premium.Api/Startup.cs:57` | Always | Opaque | Contributed by `CoreAPIStartup` (`SAIF.NetCore.ServiceExtensions`)† - not visible in source. Controller endpoint mapping happens inside this call; the relative order of routing, authentication and authorization is not visible in source |
| 6 | `app.MapGroup("/v1/RateTables").MapRateTableEndpoints()` | `src/SAIF.Premium.Api/Program.cs:22` | Always | Active | Minimal API group, registered after the `Startup` pipeline completes; see [Endpoints](endpoints.md) |
```mermaid
flowchart LR
REQ["Request"] --> SRV["IIS in-process<br/>ASP.NET Core Module V2"]
SRV --> CM["startup.ConfigureMiddleware<br/>opaque"]
CM --> SW["app.UseSwagger"]
SW --> SUI["app.UseSwaggerUI"]
SUI --> BASE["base.Configure<br/>opaque"]
BASE --> CTRL["Controller action"]
BASE --> MIN["Minimal API endpoint<br/>MapGroup /v1/RateTables"]
```
`app.UseHttpsRedirection()` is not drawn: it is commented out and does not run.
## API versioning
| Aspect | Finding |
| --- | --- |
| Package | `Asp.Versioning.Mvc` `8.1.0` and `Asp.Versioning.Mvc.ApiExplorer` `8.1.0` |
| Registration | `services.AddApiVersioning(...)` in `src/SAIF.Premium.Api/Startup.cs:33` |
| Default version | `1.0` |
| Assume default when unspecified | Yes |
| Report API versions | Yes |
| Version reader | `UrlSegmentApiVersionReader` |
| Versioned API explorer | `AddApiExplorer` - `GroupNameFormat = "'v'VVV"`, `SubstituteApiVersionInUrl = true` |
| Versions declared | `[ApiVersion("1.0")]` on `PremiumsController`; `[ApiVersion("1.0")]` and `[ApiVersion("2.0")]` on `ClassCodesController`; `AdminController` and the `RateTableEndpoints` group declare no version and use a literal `v1` route prefix |
## API documentation surface
| Aspect | Finding |
| --- | --- |
| Generator | Swashbuckle `AddSwaggerGen` |
| State | Active |
| Document(s) | `v1` and `v2`, one `SwaggerDoc` per declared API version |
| Exposed route | `swagger/v1/swagger.json` and `swagger/v2/swagger.json`, with Swagger UI at `swagger` |
| XML comments | `IncludeXmlComments("SAIF.Premium.Api.xml")` with `<GenerateDocumentationFile>true</GenerateDocumentationFile>` |
| Security definitions | None |
| Operation filters | None |
| Package and version | `Swashbuckle.AspNetCore` `6.5.0` |
## Health checks
| Aspect | Finding |
| --- | --- |
| ASP.NET Core health checks | None found |
| SAIF pingable registrations | `AddPingableTransient<IPremiumRepository, PremiumRepository>()` in `src/SAIF.Premium.Api/Startup.cs:41` |
| Ping endpoint | Not determined from source |
## Telemetry
| Aspect | Finding |
| --- | --- |
| OpenTelemetry | `.ConfigureOpenTelemetry(o => o.Owner = "Premium Systems Team")` on the host builder |
| Application Insights | None found |
| Logging configuration | `appsettings.json` `Logging:LogLevel` - `Default` = `Information`, `Microsoft.AspNetCore` = `Warning` |
| Registered in | `src/SAIF.Premium.Api/Program.cs:14` |
## Related documentation
- [API index](index.md)
- [Overview](overview.md)
- [Architecture](architecture.md)
- [Endpoints](endpoints.md)
- [Authentication and authorization](authentication-authorization.md)
- [Dependencies](dependencies.md)
- [Configuration](configuration.md)
- [Operations and processes](operations-processes.md)
- [Business rules](business-rules.md)
- [Data model](data-model.md)
- [Operational runbook](operational-runbook.md)
- [Tech debt](tech-debt.md)
> **Note:** Statements marked with † were retrieved from dependency library documentation
> via the Legends CLI RAG service. They describe inherited behavior from the indicated
> origin library and should be verified if the dependency version changes.
## Sources
- `src/SAIF.Premium.Api/Program.cs`
- `src/SAIF.Premium.Api/Startup.cs`
- `src/SAIF.Premium.Api/SAIF.Premium.Api.csproj`
- `src/SAIF.Premium.Api/web.config`
- `src/SAIF.Premium.Api/Properties/launchSettings.json`
- `src/SAIF.Premium.Api/appsettings.json`
- `src/SAIF.Premium.Api/Controllers/PremiumsController.cs`
- `src/SAIF.Premium.Api/Controllers/ClassCodesController.cs`
- `src/SAIF.Premium.Api/Controllers/AdminController.cs`
- `src/SAIF.Premium.Api/Endpoints/RateTableEndpoints.cs`