« Swashbuckle » : différence entre les versions
Apparence
(10 versions intermédiaires par le même utilisateur non affichées) | |||
Ligne 3 : | Ligne 3 : | ||
* [https://docs.microsoft.com/en-us/aspnet/core/tutorials/web-api-help-pages-using-swagger?view=aspnetcore-2.1 ASP.NET Core Web API help pages with Swagger / Open API] | * [https://docs.microsoft.com/en-us/aspnet/core/tutorials/web-api-help-pages-using-swagger?view=aspnetcore-2.1 ASP.NET Core Web API help pages with Swagger / Open API] | ||
* [https://docs.microsoft.com/en-us/aspnet/core/tutorials/getting-started-with-nswag?view=aspnetcore-2.1&tabs=visual-studio-code%2Cvisual-studio-xml Get started with NSwag and ASP.NET Core] | * [https://docs.microsoft.com/en-us/aspnet/core/tutorials/getting-started-with-nswag?view=aspnetcore-2.1&tabs=visual-studio-code%2Cvisual-studio-xml Get started with NSwag and ASP.NET Core] | ||
* [https://docs.microsoft.com/en-us/aspnet/core/tutorials/getting-started-with-swashbuckle?view=aspnetcore-5.0&tabs=visual-studio-code Swashbuckle] | |||
= Installation = | |||
{{warn | It is no longer actively maintained and has been remove from .NET 9 templates. [https://github.com/dotnet/aspnetcore/issues/54599 ]}} | |||
<kode lang='ps'> | |||
dotnet add package Swashbuckle.AspNetCore | |||
{ | |||
{ | |||
<kode lang=' | |||
</kode> | </kode> | ||
<filebox fn='Program.cs'> | <filebox fn='Program.cs'> | ||
builder.Services.AddEndpointsApiExplorer(); | builder.Services.AddEndpointsApiExplorer(); | ||
builder.Services.AddSwaggerGen(); | builder.Services.AddSwaggerGen(); | ||
Ligne 98 : | Ligne 22 : | ||
</filebox> | </filebox> | ||
= Installation Old = | |||
<kode lang='bash'> | <kode lang='bash'> | ||
dotnet add package Swashbuckle.AspNetCore | dotnet add package Swashbuckle.AspNetCore | ||
Ligne 127 : | Ligne 51 : | ||
</filebox> | </filebox> | ||
= Usage = | |||
<filebox fn='Controllers/ItemController.cs'> | <filebox fn='Controllers/ItemController.cs'> | ||
[ApiController] | [ApiController] | ||
Ligne 140 : | Ligne 64 : | ||
</filebox> | </filebox> | ||
= [https://learn.microsoft.com/en-us/aspnet/core/tutorials/getting-started-with-swashbuckle?view=aspnetcore-7.0&tabs=visual-studio-code#xml-comments XML documentation] = | |||
{{warn | {{boxx|&}} has to be escaped as {{boxx|& amp;}}}} | {{warn | {{boxx|&}} has to be escaped as {{boxx|&amp;}}}} | ||
<filebox fn='MyProject.csproj' lang='xml'> | <filebox fn='MyProject.csproj' lang='xml'> | ||
<PropertyGroup> | <PropertyGroup> | ||
Ligne 162 : | Ligne 86 : | ||
<filebox fn='Controllers/ItemController.cs'> | <filebox fn='Controllers/ItemController.cs'> | ||
/// <summary> | [Produces("application/json")] | ||
public class MyController() : ControllerBase | |||
{ | |||
/// <remarks> | /// <summary>Get all the items.</summary> | ||
/// Sample request: | /// <remarks> | ||
/// | /// Sample request: | ||
/// GET /item | /// | ||
/// </remarks> | /// GET /item | ||
/// <response code="200">Returns all the items.</response> | /// </remarks> | ||
[HttpGet] | /// <response code="200">Returns all the items.</response> | ||
[ | [HttpGet] | ||
[ProducesResponseType( | public IEnumerable<Item> GetAll() { /* ... */ } | ||
public | |||
/// <summary>Update an item.</summary> | |||
/// <param name="timeSeries">The new time series to add.</param> | |||
/// <remarks> | |||
/// Sample request: | |||
/// | |||
/// PUT /item/9 | |||
/// </remarks> | |||
[HttpPut("{id}")] | |||
[ProducesResponseType(StatusCodes.Status204NoContent)] // by default returning void or Task is associated to status code 200 | |||
public Task UpdateAsync(int id, CreateUpdateItemQuery query) { /* ... */ } | |||
</filebox> | </filebox> | ||
= [https://medium.com/@celery_liu/asp-net-core-web-api-with-swagger-api-versioning-for-dotnet-8-c8ce2fd7808c Version] = | |||
* [[Asp.net_core_8_web_api#Version|ASP.NET Core Version]] | * [[Asp.net_core_8_web_api#Version|ASP.NET Core Version]] | ||
<filebox fn='Program.cs'> | <filebox fn='Program.cs'> | ||
Ligne 223 : | Ligne 157 : | ||
</filebox> | </filebox> | ||
= Document Filter = | |||
<filebox fn='Program.cs'> | <filebox fn='Program.cs'> | ||
builder.Services.AddSwaggerGen(c => | builder.Services.AddSwaggerGen(c => | ||
Ligne 249 : | Ligne 183 : | ||
</filebox> | </filebox> | ||
== [https://vrcode.medium.com/implement-health-check-in-dot-net-core-api-and-integrate-in-swagger-5b812601cb35 Add HealthChecks endpoint] == | |||
<filebox fn='Program.cs' collapsed> | <filebox fn='Program.cs' collapsed> | ||
builder.Services.AddHealthChecks(); | builder.Services.AddHealthChecks(); | ||
Ligne 296 : | Ligne 230 : | ||
</filebox> | </filebox> | ||
= [https://docs.microsoft.com/en-us/aspnet/core/tutorials/getting-started-with-swashbuckle?view=aspnetcore-5.0&tabs=visual-studio-code#customize-the-ui Dark theme] = | |||
<filebox fn='Startup.cs'> | <filebox fn='Startup.cs'> | ||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) | ||
Ligne 393 : | Ligne 327 : | ||
</filebox> | </filebox> | ||
= [https://stackoverflow.com/questions/61881770/invalidoperationexception-cant-use-schemaid-the-same-schemaid-is-already-us The same schemaId is already used] = | |||
Occurs when you have 2 classes with the same name in different namespaces.<br> | Occurs when you have 2 classes with the same name in different namespaces.<br> | ||
Fix it by using the full class name (with namspace) for the schema id. | Fix it by using the full class name (with namspace) for the schema id. |
Dernière version du 18 janvier 2025 à 10:52
Liens
- ASP.NET Core Web API help pages with Swagger / Open API
- Get started with NSwag and ASP.NET Core
- Swashbuckle
Installation
![]() |
It is no longer actively maintained and has been remove from .NET 9 templates. [1] |
dotnet add package Swashbuckle.AspNetCore |
Program.cs |
builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); if (app.Environment.IsDevelopment()) { app.UseSwagger() .UseSwaggerUI(); } |
Installation Old
dotnet add package Swashbuckle.AspNetCore |
Startup.cs |
public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "EFWebApi", Version = "v1" }); }); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseSwagger(); app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "EFWebApi v1"); c.RoutePrefix = string.Empty; // serve the Swagger UI at the app's root (http://localhost:<port>/) }); } |
Usage
Controllers/ItemController.cs |
[ApiController] [Route("[controller]")] [Produces("application/json")] // set the Media type public class ItemController : ControllerBase { [HttpGet] [ProducesResponseType(typeof(IEnumerable<Item>), StatusCodes.Status200OK)] // set the status code and the return type public IActionResult Get() { /* ... */ } } |
XML documentation
![]() |
& has to be escaped as & |
MyProject.csproj |
<PropertyGroup> <GenerateDocumentationFile>true</GenerateDocumentationFile> </PropertyGroup> |
Program.cs |
builder.Services.AddSwaggerGen( options => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "My Web Api", Version = "v1" }); // Set the comments path for the Swagger JSON and UI. var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename)); }); } |
Controllers/ItemController.cs |
[Produces("application/json")] public class MyController() : ControllerBase { /// <summary>Get all the items.</summary> /// <remarks> /// Sample request: /// /// GET /item /// </remarks> /// <response code="200">Returns all the items.</response> [HttpGet] public IEnumerable<Item> GetAll() { /* ... */ } /// <summary>Update an item.</summary> /// <param name="timeSeries">The new time series to add.</param> /// <remarks> /// Sample request: /// /// PUT /item/9 /// </remarks> [HttpPut("{id}")] [ProducesResponseType(StatusCodes.Status204NoContent)] // by default returning void or Task is associated to status code 200 public Task UpdateAsync(int id, CreateUpdateItemQuery query) { /* ... */ } |
Version
Program.cs |
builder.Services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>(); // nuget package Asp.Versioning.Mvc.ApiExplorer builder.Services .AddApiVersioning(/* ... */) .AddApiExplorer( options => { options.GroupNameFormat = "'v'VVV"; options.SubstituteApiVersionInUrl = true; }); app.UseSwagger(); app.UseSwaggerUI( options => { var descriptions = app.DescribeApiVersions(); foreach (var description in descriptions) options.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant()); }); |
ConfigureSwaggerOptions.cs |
public class ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider) : IConfigureOptions<SwaggerGenOptions> { public void Configure(SwaggerGenOptions options) { foreach (var description in provider.ApiVersionDescriptions) { options.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description)); } static OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description) => new() { Title = "Power Analytics Model Management API", Version = description.ApiVersion.ToString(), Description = "API Description." }; } } |
Document Filter
Program.cs |
builder.Services.AddSwaggerGen(c => { c.DocumentFilter<MyDocumentFilter>(); }); |
MyDocumentFilter.cs |
public class MyDocumentFilter : IDocumentFilter { public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) { // add new property swaggerDoc.Extensions.Add("propertyName", new OpenApiObject { ["propertyName"] = new OpenApiString("value"), ["propertyName"] = new OpenApiArray { { new OpenApiString("value") } } } } } |
Add HealthChecks endpoint
Program.cs |
builder.Services.AddHealthChecks(); builder.Services.AddSwaggerGen(c => { c.DocumentFilter<HealthChecksDocumentFilter>(); }); var app = builder.Build(); app.MapHealthChecks("/health"); |
HealthChecksDocumentFilter.cs |
public class HealthChecksDocumentFilter : IDocumentFilter { public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) { var pathItem = new OpenApiPathItem(); var operation = new OpenApiOperation(); operation.Tags.Add(new OpenApiTag { Name = "ApiHealth" }); var properties = new Dictionary<string, OpenApiSchema> { { "status", new OpenApiSchema() { Type = "string" } }, { "errors", new OpenApiSchema() { Type = "array" } } }; var response = new OpenApiResponse(); response.Content.Add("application/json", new OpenApiMediaType { Schema = new OpenApiSchema { Type = "object", AdditionalPropertiesAllowed = true, Properties = properties } }); operation.Responses.Add("200", response); pathItem.AddOperation(OperationType.Get, operation); swaggerDoc.Paths.Add("/health", pathItem); } } |
Dark theme
Startup.cs |
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseStaticFiles(); app.UseSwagger(); app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "EFWebApi v1"); c.RoutePrefix = string.Empty; c.InjectStylesheet("/swagger-ui/dark-theme.css"); }); } |
wwwroot/swagger-ui/dark-theme.css |
.swagger-ui .topbar .download-url-wrapper .select-label select { border: 2px solid #89bf04;; } body { background-color: #303030; } .swagger-ui, .swagger-ui .info .title, .swagger-ui .opblock-tag, .swagger-ui section.models h4, .swagger-ui .opblock .opblock-summary-operation-id, .swagger-ui .opblock .opblock-summary-path, .swagger-ui .opblock .opblock-summary-path__deprecated, .swagger-ui table thead tr td, .swagger-ui table thead tr th, .swagger-ui .parameter__name, .swagger-ui .parameter__type, .swagger-ui .response-col_status, .swagger-ui .model-title, .swagger-ui .model, .swagger-ui .tab li { color: #f0f0f0; } .swagger-ui input[type="email"], .swagger-ui input[type="file"], .swagger-ui input[type="password"], .swagger-ui input[type="search"], .swagger-ui input[type="text"], .swagger-ui textarea { background: #303030; color: #f0f0f0; border: 1px solid gray; } .swagger-ui .opblock .opblock-section-header { background-color: #1b1b1b; } .swagger-ui .opblock .opblock-section-header h4, .swagger-ui .btn { color: #f0f0f0; } .swagger-ui input[disabled], .swagger-ui select[disabled], .swagger-ui textarea[disabled] { background-color: #303030; color: lightgray; } .swagger-ui select { background: #303030 url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="gray"><path d="M13.418 7.859a.695.695 0 01.978 0 .68.68 0 010 .969l-3.908 3.83a.697.697 0 01-.979 0l-3.908-3.83a.68.68 0 010-.969.695.695 0 01.978 0L10 11l3.418-3.141z"/></svg>') right 10px center no-repeat; color: #f0f0f0; } .swagger-ui .response-control-media-type--accept-controller select { border-color: #89bf04; } .swagger-ui .response-control-media-type__accept-message { color: #89bf04; } .arrow path { fill: gray; } .swagger-ui section.models .model-container { border: 1px solid #61affe; } .swagger-ui .model-toggle::after { background: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="gray"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg>') 50% no-repeat; } .swagger-ui .download-contents { width: unset; } |
The same schemaId is already used
Occurs when you have 2 classes with the same name in different namespaces.
Fix it by using the full class name (with namspace) for the schema id.
Program.cs |
builder.Services.AddSwaggerGen(options => { options.CustomSchemaIds(type => type.ToString()); }); |
Url
Url | Resource |
---|---|
http://localhost:<port>/swagger | swagger UI |
http://localhost:<port>/swagger/v1/swagger.json | swagger json |
Dans un projet Web API avec Visual Studio Code, ouvrir le navigateur sur la page swagger.
.vscode\launch.json |
{ "configurations": [ { "serverReadyAction": { "action": "openExternally", "pattern": "^\\s*Now listening on:\\s+(https?://\\S+)", "uriFormat": "%s/swagger" }, // autre solution "launchBrowser": { "enabled": true, "args": "${auto-detect-url}", "windows": { "command": "cmd.exe", "args": "/C start ${auto-detect-url}/swagger/index.html?url=/swagger/v1/swagger.json#!/Items" } } |
Paramètres optionnels
Swagger ne gère pas les paramètres optionnels s'ils font partie du chemin, il les considère comme des paramètres required.