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 @@ -11,6 +11,7 @@ on:
- "distributed-systems/transactional-outbox-ef-core/**"
- "aspnet-core/api-security-in-practice/**"
- "aspnet-core/passkey-first-identity/**"
- "aspnet-core/build-web-api-dotnet-8/**"
- ".github/workflows/build-samples.yml"

pull_request:
Expand All @@ -22,6 +23,7 @@ on:
- "distributed-systems/transactional-outbox-ef-core/**"
- "aspnet-core/api-security-in-practice/**"
- "aspnet-core/passkey-first-identity/**"
- "aspnet-core/build-web-api-dotnet-8/**"
- ".github/workflows/build-samples.yml"

workflow_dispatch:
Expand Down Expand Up @@ -251,3 +253,35 @@ jobs:
aspnet-core/passkey-first-identity/tests/PasskeyIdentityMinimal.Tests/PasskeyIdentityMinimal.Tests.csproj
--configuration Release
--no-build

test-web-api-fundamentals:
name: Test ASP.NET Core Todo API 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/build-web-api-dotnet-8/TodoApiMinimal.slnx

- name: Build
run: >
dotnet build
aspnet-core/build-web-api-dotnet-8/TodoApiMinimal.slnx
--configuration Release
--no-restore

- name: Test
run: >
dotnet test
aspnet-core/build-web-api-dotnet-8/TodoApiMinimal.slnx
--configuration Release
--no-build
83 changes: 47 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ Each sample folder contains a focused implementation of one tutorial topic. The
| [`distributed-systems/transactional-outbox-ef-core`](distributed-systems/transactional-outbox-ef-core/) | Minimal EF Core and SQLite demonstration that saves business state and an outbox message atomically, then publishes it through a one-shot relay | [Transactional Outbox Pattern in .NET with EF Core (.NET 10): Fix the Dual-Write Problem](https://www.dotnet-guide.com/tutorials/distributed-systems/transactional-outbox-ef-core/) |
| [`aspnet-core/api-security-in-practice`](aspnet-core/api-security-in-practice/) | Minimal JWT bearer authentication, note ownership enforcement, and rate limiting for an ASP.NET Core API | [ASP.NET Core 8 API Security: JWT Authentication, CSRF Protection & Rate Limiting](https://www.dotnet-guide.com/tutorials/aspnet-core/api-security-in-practice/) |
| [`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/) |
|
## Companion articles
| [`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/) |

## Companion articles
- [Common Microsoft.Extensions.AI mistakes](https://www.dotnet-guide.com/articles/dotnet-ai/microsoft-extensions-ai-common-mistakes/)
- [Vector search in .NET: Common mistakes](https://www.dotnet-guide.com/articles/dotnet-ai/vector-search-common-mistakes/)
- [NetArchTest, ArchUnitNET, and Roslyn analyzers](https://www.dotnet-guide.com/articles/software-architecture/netarchtest-archunitnet-roslyn-analyzers/)
Expand Down Expand Up @@ -121,43 +121,54 @@ tutorials/
| |-- TransactionalOutboxMinimal.Tests.csproj
| `-- OutboxFlowTests.cs
|-- aspnet-core/
| `-- api-security-in-practice/
| |-- ApiSecurityMinimal.slnx
| |-- README.md
| |-- src/
| | `-- ApiSecurityMinimal/
| | |-- ApiSecurityMinimal.csproj
| | |-- Program.cs
| | |-- Models.cs
| | |-- DemoStore.cs
| | |-- JwtTokenService.cs
| | `-- appsettings.json
| `-- tests/
| `-- ApiSecurityMinimal.Tests/
| |-- ApiSecurityMinimal.Tests.csproj
| `-- ApiSecurityTests.cs
| `-- passkey-first-identity/
| |-- PasskeyIdentityMinimal.slnx
| |-- api-security-in-practice/
| | |-- ApiSecurityMinimal.slnx
| | |-- README.md
| | |-- src/
| | | `-- ApiSecurityMinimal/
| | | |-- ApiSecurityMinimal.csproj
| | | |-- Program.cs
| | | |-- Models.cs
| | | |-- DemoStore.cs
| | | |-- JwtTokenService.cs
| | | `-- appsettings.json
| | `-- tests/
| | `-- ApiSecurityMinimal.Tests/
| | |-- ApiSecurityMinimal.Tests.csproj
| | `-- ApiSecurityTests.cs
| |-- passkey-first-identity/
| | |-- PasskeyIdentityMinimal.slnx
| | |-- README.md
| | |-- src/
| | | `-- PasskeyIdentityMinimal/
| | | |-- PasskeyIdentityMinimal.csproj
| | | |-- Program.cs
| | | |-- appsettings.json
| | | |-- Data/
| | | | |-- ApplicationDbContext.cs
| | | | `-- ApplicationUser.cs
| | | |-- Endpoints/
| | | | |-- AntiforgeryEndpointExtensions.cs
| | | | `-- PasskeyEndpoints.cs
| | | `-- wwwroot/
| | | |-- index.html
| | | `-- passkeys.js
| | `-- tests/
| | `-- PasskeyIdentityMinimal.Tests/
| | |-- PasskeyIdentityMinimal.Tests.csproj
| | |-- PasskeyEndpointTests.cs
| | `-- PasskeyIdentityFactory.cs
| `-- build-web-api-dotnet-8/
| |-- TodoApiMinimal.slnx
| |-- README.md
| |-- src/
| | `-- PasskeyIdentityMinimal/
| | |-- PasskeyIdentityMinimal.csproj
| | |-- Program.cs
| | |-- appsettings.json
| | |-- Data/
| | | |-- ApplicationDbContext.cs
| | | `-- ApplicationUser.cs
| | |-- Endpoints/
| | | |-- AntiforgeryEndpointExtensions.cs
| | | `-- PasskeyEndpoints.cs
| | `-- wwwroot/
| | |-- index.html
| | `-- passkeys.js
| | `-- TodoApiMinimal/
| | |-- TodoApiMinimal.csproj
| | `-- Program.cs
| `-- tests/
| `-- PasskeyIdentityMinimal.Tests/
| |-- PasskeyIdentityMinimal.Tests.csproj
| |-- PasskeyEndpointTests.cs
| `-- PasskeyIdentityFactory.cs
| `-- TodoApiMinimal.Tests/
| |-- TodoApiMinimal.Tests.csproj
| `-- TodoApiTests.cs
|-- .github/
| `-- workflows/
| `-- build-samples.yml
Expand Down
166 changes: 166 additions & 0 deletions aspnet-core/build-web-api-dotnet-8/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
# ASP.NET Core Todo API — Minimal Fundamentals Sample

A focused .NET 10 companion showing how core ASP.NET Core Web API building
blocks work together in a small Todo API.

## Full tutorial

[ASP.NET Core Fundamentals: Build Web APIs on .NET 8](https://www.dotnet-guide.com/tutorials/aspnet-core/build-web-api-dotnet-8/)

## 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 sample demonstrates the same foundational
routing, dependency-injection, request-binding, middleware, CRUD, and HTTP-result
patterns.

## What this sample demonstrates

- Minimal API route groups;
- route parameters and constraints;
- JSON request-body binding;
- dependency injection into route handlers;
- an in-memory repository;
- GET, POST, PUT, and DELETE operations;
- validation responses;
- `200`, `201`, `204`, `400`, and `404` semantics;
- a `Location` header after resource creation;
- a small request-ID middleware;
- integration testing with `WebApplicationFactory<Program>`.

## Important boundary

This repository sample intentionally does not reproduce the complete tutorial.

The full DOTNET GUIDE tutorial also covers:

- controllers and MVC patterns;
- configuration sources;
- EF Core and SQLite;
- migrations;
- JWT authentication and authorization;
- caching;
- rate limiting;
- health-check strategy;
- performance guidance;
- migration from ASP.NET Core 7;
- deployment options.

The separate repository sample
[`aspnet-core/api-security-in-practice`](../api-security-in-practice/)
contains a focused security example.

## Prerequisite

- .NET 10 SDK

Check:

```powershell
dotnet --version
```

## Restore, build, and test

```powershell
dotnet restore .\TodoApiMinimal.slnx

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

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

## Run

```powershell
dotnet run `
--project .\src\TodoApiMinimal\TodoApiMinimal.csproj `
--urls http://localhost:5128
```

## Endpoints

```text
GET /
GET /health
GET /api/todos
GET /api/todos/{id}
POST /api/todos
PUT /api/todos/{id}
DELETE /api/todos/{id}
```

## Example request

```powershell
$body = @{
title = "Review HTTP status codes"
} | ConvertTo-Json

Invoke-RestMethod `
-Method Post `
-Uri "http://localhost:5128/api/todos" `
-ContentType "application/json" `
-Body $body
```

## Persistence limitation

The repository is stored only in application memory.

Restarting the process resets the sample to its two seeded Todo items.

No production persistence is implied.

## Project structure

```text
TodoApiMinimal.slnx
README.md
src/
└── TodoApiMinimal/
├── TodoApiMinimal.csproj
└── Program.cs
tests/
└── TodoApiMinimal.Tests/
├── TodoApiMinimal.Tests.csproj
└── TodoApiTests.cs
```

## Deliberately omitted

- controllers;
- EF Core;
- SQLite;
- migrations;
- authentication;
- authorization;
- CORS;
- caching;
- rate limiting;
- Swagger or OpenAPI UI;
- Docker;
- cloud deployment;
- production storage.

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

## Verification

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

This sample is educational and should be reviewed before production use.
8 changes: 8 additions & 0 deletions aspnet-core/build-web-api-dotnet-8/TodoApiMinimal.slnx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Solution>
<Folder Name="/src/">
<Project Path="src/TodoApiMinimal/TodoApiMinimal.csproj" />
</Folder>
<Folder Name="/tests/">
<Project Path="tests/TodoApiMinimal.Tests/TodoApiMinimal.Tests.csproj" />
</Folder>
</Solution>
Loading
Loading