Une gestion d’erreur de son API .Net automatique via ses exceptions
...
Depuis .NET 9, le le support d’OpenAPI est directement inclus dans .NET et ne passe plus par les librairies Swagger par défaut (plus d’info sur ce choix ici si jamais ça vous intéresse).
De façons simplifiée, la librairie Swashbuckle.AspNetCore.Swagger faisait deux choses:
On va voir dans cet article comment se mettre à la page facilement 🙂
Pro-tips: Si vous utilisez l’authentification dans Swagger, je vous conseille d’exporter le JSON généré ce qui va nous aider pour la suite.
Pour se faire allez sur votre UI Swagger, cliquez sur le lien de votre json et gardez le de côté:
Sommaire
Si vous êtes passé à .NET 9, vous avez déjà tout ce dont on a besoin 👍Supprimez les dépendances aux libs swashbuckle
Vérifiez que vous avez bien une dépendance à Microsoft.AspNetCore.OpenApi avec une version supérieure à 9.0.0
On va supprimer toute la configuration qui était propre à Swagger. Si vous buildez votre projet, vous aurez toutes les portions à enlever:
Dans mon cas, ça revient à enlever:
(gardez bien le “if”, on en aura toujours besoin)
et
Note: Si vous avez comme moi des portions du type AddSecurityDefinition et AddSecurityRequirement, mettez les de côté, on va s’en servir plus tard.
Si le projet Build à ce stade, c’est que vous n’avez rien oublié 👍
Dans program.cs, on va rajouter:
builder.Services.AddOpenApi();
Une fois fait, là où on avait
app.UseSwagger(); app.UseSwaggerUI();
On va mettre:
app.MapOpenApi();
C’est ce qui va nous permettre de générer notre open.api
Note: Si a ce stade vous lancez votre application et que vous allez sur openapi/v1.json vous verrez le json généré:
Si sur Swagger vous passiez le bearer token via l’UI avec cette modale:
On va voir comment faire l’équivalent (ça marchera peu importe l’UI que vous allez choisir à l’étape d’après).
On va recréer la même configuration dans notre nouveau JSON pour avoir l’option du bearer token dans notre UI (peu importe celle choisie).
Créez une classe BearerSecuritySchemeTransformer qui suit la doc Microsoft:
Al’heure actuelle ça donne:
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.OpenApi; using Microsoft.Extensions.DependencyInjection; using Microsoft.OpenApi.Models; internal sealed class BearerSecuritySchemeTransformer(IAuthenticationSchemeProvider authenticationSchemeProvider) : IOpenApiDocumentTransformer { public async Task TransformAsync(OpenApiDocument document, OpenApiDocumentTransformerContext context, CancellationToken cancellationToken) { var authenticationSchemes = await authenticationSchemeProvider.GetAllSchemesAsync(); if (authenticationSchemes.Any(authScheme => authScheme.Name == "Bearer")) { var requirements = new Dictionary<string, OpenApiSecurityScheme> { ["Bearer"] = new OpenApiSecurityScheme { Type = SecuritySchemeType.Http, Scheme = "bearer", // "bearer" refers to the header name here In = ParameterLocation.Header, BearerFormat = "Json Web Token" } }; document.Components ??= new OpenApiComponents(); document.Components.SecuritySchemes = requirements; } } }
On va ensuite l’adapter:
Changez la condition:
if (authenticationSchemes.Any(authScheme => authScheme.Name == "Bearer"))
par votre schéma d’authentification.
Vous pourrez le voir dans votre program.cs suivant votre configuration de Bearer quelque chose du type
builder.Services.AddAuthentication().AddJwtBearer()
=> là ça sera Bearer
pas besoin de changer
builder.Services.AddAuthentication().AddBearerToken(IdentityConstants.BearerScheme);
, là ça sera IdentityConstants.BearerScheme
Dans les autres cas, pas de panique, mettez un point d’arrêt au niveau de la condition et vous verrez en debug votre schéma utilisé 😉
On va modifier les requirements par ceux qu’on avait pour Swagger. Dans mon cas j’avais:
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme() { Name = "Authorization", Type = SecuritySchemeType.ApiKey, Scheme = "Bearer", In = ParameterLocation.Header, Description = "Bearer token" });
Je fais mon plus beau copier/coller et ça donne:
if (authenticationSchemes.Any(authScheme => authScheme.Name == IdentityConstants.BearerScheme)) { var requirements = new Dictionary<string, OpenApiSecurityScheme> { ["Bearer"] = new() { Name = "Authorization", Type = SecuritySchemeType.ApiKey, In = ParameterLocation.Header, Description = "Bearer token" } };
Si on avait des security requirements:
c.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } }, new string[] {} } });
Même combat, copie/colle dans une variable
var securityRequirement = new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } }, new string[] {} } };
et on rajoute document.SecurityRequirements.Add(securityRequirement);
à la fin de notre méthode.
Voici ma classe finale (je vous conseille de la mettre dans un fichier dédié histoire de ne pas trop polluer votre program.cs)
internal sealed class BearerSecuritySchemeTransformer(IAuthenticationSchemeProvider authenticationSchemeProvider) : IOpenApiDocumentTransformer { public async Task TransformAsync(OpenApiDocument document, OpenApiDocumentTransformerContext context, CancellationToken cancellationToken) { var authenticationSchemes = await authenticationSchemeProvider.GetAllSchemesAsync(); if (authenticationSchemes.Any(authScheme => authScheme.Name == IdentityConstants.BearerScheme)) { var requirements = new Dictionary<string, OpenApiSecurityScheme> { ["Bearer"] = new() { Name = "Authorization", Type = SecuritySchemeType.ApiKey, In = ParameterLocation.Header, Description = "Bearer token" } }; var securityRequirement =new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } }, new string[] {} } }; document.Components ??= new OpenApiComponents(); document.Components.SecuritySchemes = requirements; document.SecurityRequirements.Add(securityRequirement); } } }
On va ensuite charger notre classe en rajoutant dans program.cs:
builder.Services.AddOpenApi(options => { options.AddDocumentTransformer<BearerSecuritySchemeTransformer>(); });
en dessous de builder.Services.AddAuthentication().xxx()
Pour vérifier que tout a bien fonctionné, regardez le fichier swagger.json que vous aviez exporté et comparez le à votre json actuel dans openapi/v1.json. Si les parties securitySchemes et security sont identiques alors vous aurez bien l’option pour ajouter un token dans votre UI.
Il y a plusieurs outils qui permettent “d’ingérer” ce json et d’en faire une belle interface web. A l’heure actuelle, Microsoft n’a pas prévu d’en embarquer par défaut, c’est à vous de choisir lequel utiliser.
Ici je vais présenter deux alternatives:
Pour se faire, le plus simple c’est d’installer Scalar.AspNetCore:
Puis de rajouter:
app.MapScalarApiReference();
en dessous de app.MapOpenApi();
Si vous allez sur /scalar/v1 vous aurez votre UI:
Note: Si vous avez suivi la partie sur le Bearer Token, vous devriez voir la section APIKEY sous la base URL. Il suffit de coller le bearer token pour que ça fonctionne et c’est tout.
Si comme moi, lorsque vous lanciez votre projet, vous étiez directement sur swagger/index.html, il faut changer votre configuration pour cibler la nouvelle adresse qui est scalar/v1 par défaut.
Pour se faire, c’est super simple, allez dans votre fichier launchSettings.json et remplacez swagger par scalar/v1
On va rajouter uniquement uniquement la partie UI de Swagger en installant:
Swashbuckle.AspNetCore.SwaggerUI
Une fois fait, on va simplement lui indiquer le JSON à “ingérer”:
Dans program.cs, ajoutez la ligne suivante en dessous de app.MapOpenApi();
app.UseSwaggerUI(options => { options.ConfigObject.Urls = [new UrlDescriptor { Name = "Your app name v1", Url = "/openapi/v1.json" }]; ; });
Si vous allez sur /swagger dans votre application, vous ne verrez même pas la différence avec votre ancienne configuration:
Note: Si vous avez suivi la partie sur le Bearer Token, vous devriez voir le bouton Authorize pour coller votre bearer token.
On va voir comment avoir en quelques minutes des assertions qui vont vérifier les endpoints de notre API avec des scénarios de ce genre: Feature: Create a new account As a visitor, I can create an account to access the game Scenario: A visitor c...
Description du problème Par défaut, il n’est pas autorisé de faire des requêtes entre une application qui est dans un domaine A vers une autre qui serait dans un domaine B (pour des raisons de sécurité, il y a plus de détails dans les sources). S...
Image prise sur Windows Central Historique Pour cet article je vais faire un mini REX sur l’écosystème de Microsoft pour les développeurs. J’ai commencé à travailler avec les technologies Microsoft vers 2016. Pour situer, Windows 10 devait avoir �...