Développement Web

Minimal APIs en .NET 10 : validation, OpenAPI 3.1, SSE et Native AOT

8 min de lecture

Pourquoi Minimal APIs s’imposent comme le standard ASP.NET Core en 2026

Depuis leur introduction dans .NET 6, les Minimal APIs ont parcouru un long chemin. Présentées initialement comme une alternative légère aux contrôleurs MVC, elles sont devenues en .NET 10 la voie privilégiée pour exposer des services HTTP : démarrage plus rapide, empreinte mémoire réduite, compatibilité Native AOT excellente, et désormais une intégration complète de la validation déclarative et des Server-Sent Events. Microsoft documente ces ajouts dans les release notes officielles d’ASP.NET Core 10.0 publiées sur learn.microsoft.com.

Cette série pratique s’adresse à un développeur qui connaît déjà C# et veut bâtir des API REST robustes pour la production. Nous partons d’un projet vide, montons une API CRUD complète, ajoutons la validation, l’authentification par token, la documentation OpenAPI 3.1 en YAML, les Server-Sent Events pour des flux temps réel, et terminons par le déploiement en mode framework-dependent puis Native AOT. Chaque étape est justifiée, chaque commande accompagnée de son résultat attendu.

Étape 1 : Préparer l’environnement .NET 10

Téléchargez le SDK .NET 10 depuis https://get.dot.net/10. Sur Linux/macOS, exécutez le script officiel. Sur Windows, l’installeur MSI est plus simple. Vérifiez ensuite votre version :

dotnet --version

La sortie doit afficher 10.0.x. Si vous voyez encore 9.0.x, c’est que le PATH pointe vers l’ancien SDK ; corrigez via which dotnet sur Unix ou where dotnet sur Windows. Le SDK .NET 10 ajoute aussi des scripts de complétion natifs pour bash, zsh, fish et PowerShell : activez-les via dotnet completions install bash pour profiter de l’auto-complétion des commandes et des arguments. Signal de réussite : taper dotnet bui[TAB] propose automatiquement build.

Étape 2 : Créer un projet web vide

Dans un dossier de travail, exécutez :

dotnet new web -n CatalogueApi
cd CatalogueApi
dotnet run

Le template web génère le projet le plus dépouillé possible : un seul fichier Program.cs avec une route GET /. Après dotnet run, ouvrez votre navigateur sur l’URL affichée (typiquement http://localhost:5000) et vérifiez l’affichage Hello World!. Le projet pèse environ 200 lignes de configuration générées, contre 600+ pour un template MVC équivalent. Cette légèreté est l’argument principal des Minimal APIs : moins de cérémonie, plus de lisibilité.

Étape 3 : Définir un modèle et un endpoint CRUD

Remplacez le contenu de Program.cs par :

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

var produits = new List<Produit>
{
    new(1, "Casque audio", 49.90m),
    new(2, "Clavier sans fil", 79.00m)
};

app.MapGet("/produits", () => produits);
app.MapGet("/produits/{id:int}", (int id) =>
    produits.FirstOrDefault(p => p.Id == id) is { } p
        ? Results.Ok(p)
        : Results.NotFound());

app.MapPost("/produits", (Produit nouveau) =>
{
    produits.Add(nouveau);
    return Results.Created($"/produits/{nouveau.Id}", nouveau);
});

app.Run();

public record Produit(int Id, string Nom, decimal Prix);

Lancez dotnet run et testez les trois routes avec curl :

curl http://localhost:5000/produits
curl http://localhost:5000/produits/1
curl -X POST -H "Content-Type: application/json" \
  -d '{"id":3,"nom":"Souris ergonomique","prix":29.50}' \
  http://localhost:5000/produits

La syntaxe {id:int} applique une contrainte de route : un appel à /produits/abc renvoie automatiquement 404 sans entrer dans le handler. Le pattern matching is { } p avec déconstruction de propriété est une feature C# qui rend le code idiomatique. Signal de réussite : le POST retourne un statut 201 Created avec un header Location: /produits/3.

Étape 4 : Activer la validation déclarative

C’est l’une des grandes nouveautés d’ASP.NET Core 10. Ajoutez le service de validation et les attributs System.ComponentModel.DataAnnotations :

builder.Services.AddValidation();

Modifiez ensuite l’endpoint POST :

app.MapPost("/produits",
    ([System.ComponentModel.DataAnnotations.Required] string nom,
     [System.ComponentModel.DataAnnotations.Range(0.01, 10000)] decimal prix) =>
{
    var p = new Produit(produits.Count + 1, nom, prix);
    produits.Add(p);
    return Results.Created($"/produits/{p.Id}", p);
});

Un appel POST avec prix: -5 renvoie maintenant une réponse 400 Bad Request structurée selon RFC 7807 (ProblemDetails) :

{
  "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "errors": { "prix": ["The field prix must be between 0.01 and 10000."] }
}

Avant .NET 10, cette validation nécessitait des bibliothèques tierces comme FluentValidation ou MiniValidation. Désormais elle est native et compatible Native AOT grâce à des source generators. Signal de réussite : un payload invalide reçoit 400 sans le moindre boilerplate dans votre code.

Étape 5 : Générer une documentation OpenAPI 3.1 en YAML

OpenAPI est devenu indispensable pour exposer une API auprès d’équipes mobiles, frontend ou partenaires externes. ASP.NET Core 10 supporte la version OpenAPI 3.1 et expose les documents en YAML en plus du JSON. Ajoutez :

builder.Services.AddOpenApi(options =>
{
    options.OpenApiVersion = Microsoft.OpenApi.OpenApiSpecVersion.OpenApi3_1;
});

app.MapOpenApi();
app.MapOpenApi("/openapi/{documentName}.yaml");

Lancez l’application et visitez http://localhost:5000/openapi/v1.json puis http://localhost:5000/openapi/v1.yaml. Vous obtenez la spécification complète, y compris les contraintes de validation déclarées à l’étape 4. Pour intégrer la documentation XML des handlers, ajoutez à votre .csproj :

<PropertyGroup>
    <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>

Puis transformez vos lambdas en méthodes nommées avec des commentaires XML — les commentaires <summary> et <param> seront automatiquement injectés dans la spec OpenAPI. Signal de réussite : Swagger UI affiche les descriptions de paramètres telles qu’écrites dans votre code C#.

Étape 6 : Streamer des données en temps réel avec Server-Sent Events

Avant .NET 10, exposer un flux d’événements depuis le serveur exigeait SignalR ou des manipulations manuelles d’écriture HTTP. ASP.NET Core 10 offre maintenant TypedResults.ServerSentEvents pour servir un IAsyncEnumerable en SSE :

app.MapGet("/rythme-cardiaque", (CancellationToken ct) =>
{
    async IAsyncEnumerable<object> FluxAsync(
        [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken token)
    {
        while (!token.IsCancellationRequested)
        {
            yield return new { bpm = Random.Shared.Next(60, 100), at = DateTime.UtcNow };
            await Task.Delay(2000, token);
        }
    }
    return TypedResults.ServerSentEvents(FluxAsync(ct), eventType: "heartRate");
});

Testez depuis un terminal :

curl -N http://localhost:5000/rythme-cardiaque

L’option -N désactive le buffering. Vous voyez défiler des événements event: heartRate toutes les 2 secondes. Pour un tableau de bord temps réel, c’est dix fois plus simple que SignalR, à condition que le flux soit unidirectionnel (serveur vers client). Le navigateur consomme ce flux nativement via l’API EventSource. Signal de réussite : Ctrl+C interrompt proprement le stream et libère la ressource serveur.

Étape 7 : Sécuriser avec JWT Bearer

Pour la production, protéger les routes d’écriture est obligatoire. Ajoutez le package :

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

Configurez l’authentification :

builder.Services.AddAuthentication("Bearer")
    .AddJwtBearer(opt =>
    {
        opt.TokenValidationParameters = new()
        {
            ValidateIssuer = true,
            ValidIssuer = "itskillscenter",
            ValidateAudience = true,
            ValidAudience = "catalogue-api",
            ValidateLifetime = true,
            IssuerSigningKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(
                System.Text.Encoding.UTF8.GetBytes(
                    builder.Configuration["Jwt:Secret"]!))
        };
    });
builder.Services.AddAuthorization();
// ...
app.UseAuthentication();
app.UseAuthorization();

app.MapPost("/produits", /* ... */).RequireAuthorization();

Stockez la clé secrète dans appsettings.Development.json (jamais en clair dans Git !) ou mieux, dans le User Secrets store via dotnet user-secrets set Jwt:Secret "...". Pour produire un token de test, utilisez l’outil intégré dotnet user-jwts create, introduit en .NET 7 et toujours présent. Signal de réussite : un POST sans header Authorization: Bearer ... renvoie 401, et avec un token valide renvoie 201.

Étape 8 : Déployer en mode framework-dependent puis Native AOT

Le déploiement standard reste le plus simple :

dotnet publish -c Release -o ./publish
dotnet ./publish/CatalogueApi.dll

Pour profiter du démarrage instantané et de l’empreinte mémoire minimale, activez Native AOT en ajoutant au .csproj :

<PropertyGroup>
    <PublishAot>true</PublishAot>
    <InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>

Publiez ensuite :

dotnet publish -c Release -r linux-x64

Le binaire généré pèse environ 18 Mo (contre 60+ Mo pour le framework-dependent), démarre en moins de 50 ms et consomme typiquement 25-40 Mo de RSS au repos. Attention : Native AOT impose le trimming, ce qui interdit Assembly.LoadFile, System.Reflection.Emit et les serializers basés sur reflection. Les nouveaux source generators de System.Text.Json couvrent la plupart des cas, mais si vous utilisez Newtonsoft.Json ou des bibliothèques dynamiques, vous devrez les remplacer ou rester en framework-dependent. Signal de réussite : ./CatalogueApi --version affiche la chaîne configurée et la commande file CatalogueApi indique ELF 64-bit executable.

Pour la suite : observabilité, rate limiting, et tests

Une API qui part en production demande plus que des routes et un token. Pensez à ajouter le rate limiting (builder.Services.AddRateLimiter, intégré nativement depuis .NET 7), l’export OpenTelemetry pour les métriques et les traces, et des tests d’intégration avec WebApplicationFactory. Si votre charge croît, .NET Aspire 13.1 orchestre votre API avec Redis, PostgreSQL et un dashboard de télémétrie en quelques lignes de configuration. Le projet de catalogue construit ici peut évoluer naturellement vers ces blocs sans réécriture, parce que les Minimal APIs partagent le même pipeline middleware que MVC : tout ce que vous ajoutez aujourd’hui s’appliquera demain à plus grande échelle.

Service ITSkillsCenter

Site ou application web sur mesure

Conception Pro + Nom de domaine 1 an + Hébergement 1 an + Formation + Support 6 mois. Accès et code livrés. À partir de 350 000 FCFA.

Demander un devis
Publicité