« Swashbuckle » : différence entre les versions
Apparence
(15 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] | |||
= [https:// | = 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=' | <kode lang='ps'> | ||
dotnet add package Swashbuckle.AspNetCore | |||
dotnet add package | |||
</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;}}}} | |||
<filebox fn='MyProject.csproj' lang='xml'> | <filebox fn='MyProject.csproj' lang='xml'> | ||
<PropertyGroup> | <PropertyGroup> | ||
Ligne 161 : | 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 222 : | Ligne 157 : | ||
</filebox> | </filebox> | ||
= Document Filter = | |||
<filebox fn='Program.cs'> | <filebox fn='Program.cs'> | ||
builder.Services.AddSwaggerGen(c => | builder.Services.AddSwaggerGen(c => | ||
Ligne 248 : | 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 295 : | 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 390 : | Ligne 325 : | ||
width: unset; | width: unset; | ||
} | } | ||
</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> | |||
Fix it by using the full class name (with namspace) for the schema id. | |||
<filebox fn='Program.cs'> | |||
builder.Services.AddSwaggerGen(options => | |||
{ | |||
options.CustomSchemaIds(type => type.ToString()); | |||
}); | |||
</filebox> | </filebox> | ||
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.