Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .github/workflows/build-samples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ on:
- "aspnet-core/passkey-first-identity/**"
- "aspnet-core/build-web-api-dotnet-8/**"
- "aspnet-core/caching-outputcache-redis-invalidation/**"
- "aspnet-core/minimal-apis-real-world/**"
- ".github/workflows/build-samples.yml"

pull_request:
Expand All @@ -26,6 +27,7 @@ on:
- "aspnet-core/passkey-first-identity/**"
- "aspnet-core/build-web-api-dotnet-8/**"
- "aspnet-core/caching-outputcache-redis-invalidation/**"
- "aspnet-core/minimal-apis-real-world/**"
- ".github/workflows/build-samples.yml"

workflow_dispatch:
Expand Down Expand Up @@ -319,3 +321,35 @@ jobs:
aspnet-core/caching-outputcache-redis-invalidation/OutputCacheCatalogMinimal.slnx
--configuration Release
--no-build

test-minimal-api-pipeline:
name: Test real-world Minimal API pipeline sample
runs-on: ubuntu-latest

steps:
- name: Check out repository
uses: actions/checkout@v5

- name: Install .NET 10 SDK
uses: actions/setup-dotnet@v5
with:
dotnet-version: "10.0.x"

- name: Restore
run: >
dotnet restore
aspnet-core/minimal-apis-real-world/MinimalApiPipeline.slnx

- name: Build
run: >
dotnet build
aspnet-core/minimal-apis-real-world/MinimalApiPipeline.slnx
--configuration Release
--no-restore

- name: Test
run: >
dotnet test
aspnet-core/minimal-apis-real-world/MinimalApiPipeline.slnx
--configuration Release
--no-build
26 changes: 19 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Each sample folder contains a focused implementation of one tutorial topic. The
| [`aspnet-core/passkey-first-identity`](aspnet-core/passkey-first-identity/) | Minimal .NET 10 Identity sample for passkey enrollment, username-first passkey sign-in, antiforgery protection, and stored credential listing | [Passkeys in ASP.NET Core Identity (.NET 10): Build a Passwordless-First Web App with WebAuthn](https://www.dotnet-guide.com/tutorials/aspnet-core/passkey-first-identity/) |
| [`aspnet-core/build-web-api-dotnet-8`](aspnet-core/build-web-api-dotnet-8/) | Minimal .NET 10 Todo API demonstrating route groups, dependency injection, CRUD status codes, validation, middleware, and integration testing | [ASP.NET Core Fundamentals: Build Web APIs on .NET 8](https://www.dotnet-guide.com/tutorials/aspnet-core/build-web-api-dotnet-8/) |
| [`aspnet-core/caching-outputcache-redis-invalidation`](aspnet-core/caching-outputcache-redis-invalidation/) | Minimal .NET 10 Catalog API demonstrating Output Cache policies, query and route variation, tag eviction, write-path invalidation, and integration testing | [ASP.NET Core Caching: Output Cache, Redis & Invalidation Strategies That Actually Work](https://www.dotnet-guide.com/tutorials/aspnet-core/caching-outputcache-redis-invalidation/) |
| [`aspnet-core/minimal-apis-real-world`](aspnet-core/minimal-apis-real-world/) | Focused .NET 10 Orders API demonstrating endpoint filters, FluentValidation, URL-segment versioning, typed results, and partitioned rate limiting | [ASP.NET Core / Minimal APIs in the Real World: Filters, Validation, Versioning & Rate Limiting](https://www.dotnet-guide.com/tutorials/aspnet-core/minimal-apis-real-world/) |

## Companion articles
- [Common Microsoft.Extensions.AI mistakes](https://www.dotnet-guide.com/articles/dotnet-ai/microsoft-extensions-ai-common-mistakes/)
Expand Down Expand Up @@ -170,17 +171,28 @@ tutorials/
| | `-- TodoApiMinimal.Tests/
| | |-- TodoApiMinimal.Tests.csproj
| | `-- TodoApiTests.cs
| `-- caching-outputcache-redis-invalidation/
| |-- OutputCacheCatalogMinimal.slnx
| |-- caching-outputcache-redis-invalidation/
| | |-- OutputCacheCatalogMinimal.slnx
| | |-- README.md
| | |-- src/
| | | `-- OutputCacheCatalogMinimal/
| | | |-- OutputCacheCatalogMinimal.csproj
| | | `-- Program.cs
| | `-- tests/
| | `-- OutputCacheCatalogMinimal.Tests/
| | |-- OutputCacheCatalogMinimal.Tests.csproj
| | `-- OutputCacheTests.cs
| `-- minimal-apis-real-world/
| |-- MinimalApiPipeline.slnx
| |-- README.md
| |-- src/
| | `-- OutputCacheCatalogMinimal/
| | |-- OutputCacheCatalogMinimal.csproj
| | `-- MinimalApiPipeline/
| | |-- MinimalApiPipeline.csproj
| | `-- Program.cs
| `-- tests/
| `-- OutputCacheCatalogMinimal.Tests/
| |-- OutputCacheCatalogMinimal.Tests.csproj
| `-- OutputCacheTests.cs
| `-- MinimalApiPipeline.Tests/
| |-- MinimalApiPipeline.Tests.csproj
| `-- MinimalApiPipelineTests.cs
|-- .github/
| `-- workflows/
| `-- build-samples.yml
Expand Down
8 changes: 8 additions & 0 deletions aspnet-core/minimal-apis-real-world/MinimalApiPipeline.slnx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Solution>
<Folder Name="/src/">
<Project Path="src/MinimalApiPipeline/MinimalApiPipeline.csproj" />
</Folder>
<Folder Name="/tests/">
<Project Path="tests/MinimalApiPipeline.Tests/MinimalApiPipeline.Tests.csproj" />
</Folder>
</Solution>
202 changes: 202 additions & 0 deletions aspnet-core/minimal-apis-real-world/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
# Real-World Minimal API Pipeline

A focused .NET 10 companion demonstrating endpoint filters, FluentValidation,
URL-segment API versioning, typed results, and partitioned rate limiting in an
ASP.NET Core Minimal API.

## Full tutorial

[ASP.NET Core / Minimal APIs in the Real World: Filters, Validation, Versioning & Rate Limiting](https://www.dotnet-guide.com/tutorials/aspnet-core/minimal-apis-real-world/)

## Framework note

The tutorial explains ASP.NET Core 8.

This companion targets .NET 10 so it can use the DOTNET GUIDE repository's
current SDK and CI toolchain. The endpoint-filter, FluentValidation,
URL-versioning, typed-result, and rate-limiting patterns demonstrated here are
the same core ASP.NET Core concepts.

## What this sample demonstrates

- route groups;
- endpoint filters;
- validation before handler execution;
- RFC 7807 validation responses;
- API v1 and v2 using URL segments;
- different response contracts over one repository;
- typed HTTP results;
- a fixed-window write limiter;
- independent rate-limit partitions;
- integration tests with `WebApplicationFactory<Program>`.

## Request pipeline

```text
request
-> version selection
-> rate limiting
-> timing filter
-> validation filter
-> endpoint handler
-> typed response
```

## Versions

### V1

```text
GET /api/v1/orders
GET /api/v1/orders/{id}
POST /api/v1/orders
```

V1 returns item names directly.

### V2

```text
GET /api/v2/orders?page=1&pageSize=2
GET /api/v2/orders/{id}
```

V2 returns a paginated envelope and exposes item counts.

## Demonstration rate-limit partition

The create endpoint permits two requests per minute for each distinct
`X-Client-Id` value.

This header is used only to make partition behavior easy to test.

It is not authentication and must not be trusted as a production identity.
Production partition keys should come from validated identities, API keys,
tenants, or trusted network signals.

## Important boundary

This repository sample intentionally does not reproduce the complete tutorial.

The full tutorial also covers:

- JWT authentication;
- scope and role authorization;
- OpenAPI and Swagger;
- response and output caching;
- structured logging;
- OpenTelemetry;
- health checks;
- Kestrel tuning;
- Docker;
- production deployment.

Dedicated repository companions already cover API security and Output Cache.

## Persistence limitation

Orders are stored only in process memory.

Restarting the application restores the three seeded orders and resets all
rate-limit partitions.

## Prerequisite

- .NET 10 SDK

Check:

```powershell
dotnet --version
```

## Restore, build, and test

```powershell
dotnet restore `
.\MinimalApiPipeline.slnx

dotnet build `
.\MinimalApiPipeline.slnx `
--configuration Release `
--no-restore

dotnet test `
.\MinimalApiPipeline.slnx `
--configuration Release `
--no-build
```

## Run

```powershell
dotnet run `
--project .\src\MinimalApiPipeline\MinimalApiPipeline.csproj `
--urls http://localhost:5136
```

## Example valid request

```powershell
$headers = @{
"X-Client-Id" = "powershell-demo"
}

$body = @{
customerId = "customer-400"
items = @(
"microphone"
)
} | ConvertTo-Json

Invoke-RestMethod `
-Method Post `
-Uri "http://localhost:5136/api/v1/orders" `
-Headers $headers `
-ContentType "application/json" `
-Body $body
```

## Project structure

```text
MinimalApiPipeline.slnx
README.md
src/
`-- MinimalApiPipeline/
|-- MinimalApiPipeline.csproj
`-- Program.cs
tests/
`-- MinimalApiPipeline.Tests/
|-- MinimalApiPipeline.Tests.csproj
`-- MinimalApiPipelineTests.cs
```

## Deliberately omitted

- authentication;
- authorization;
- Swagger;
- OpenAPI generation;
- caching;
- database persistence;
- Redis;
- OpenTelemetry exporters;
- Docker;
- cloud deployment;
- production identity partitioning.

These are covered by the full tutorial or another focused sample.

## Verification

- Companion target framework: .NET 10
- Tutorial framework: .NET 8
- External services required: none
- Database required: none
- API keys required: none
- Expected integration tests: 8
- Last reviewed: 2026-08-01

This sample is educational and should be reviewed and load-tested before
production use.
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference
Include="Asp.Versioning.Http"
Version="10.0.1" />

<PackageReference
Include="FluentValidation.DependencyInjectionExtensions"
Version="12.1.1" />
</ItemGroup>

</Project>
Loading
Loading