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:
Générer un JSON au format OpenApi en scannant votre projet (c’est ce qui est inclus dans .NET maintenant)
Prendre ce JSON et générer une UI à partir de celui-ci (là vous aurez plusieurs choix à votre disposition)
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
I. Les dépendances
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
II. Supprimer la configuration swagger
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é 👍
III. Générer le json OpenAPI
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é:
(Optionnel) Passer le Bearer Token
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:
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.
IV. Installer l’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.
Swagger UI: malgré le titre de l’article, on a toujours le droit d’utiliser la couche UI de Swagger ce qui nous permet de faire le changement de façon transparente pour nos utilisateurs 🙂
1. Scalar
Pour se faire, le plus simple c’est d’installer Scalar.AspNetCore:
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.
(Optionnel) Remplacer la run-conf si vous alliez directement sur Swagger
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
2. Swagger UI
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 creates an account
When I fill the login form with
| email | password |
| [email protected] | Jh0...
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 �...
Photo by Jackson So on Unsplash
Dans ce court article je vais vous expliquer comment faire en sorte que votre formulaire généré avec le moteur de templating Razor ait bien le bon champ « action » et non un champ « action » vide.
...