Aller au contenu

« Swashbuckle » : différence entre les versions

De Banane Atomic
 
(6 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://docs.microsoft.com/en-us/aspnet/core/tutorials/getting-started-with-swashbuckle?view=aspnetcore-5.0&tabs=visual-studio-code Swashbuckle] =
= Installation =
== Swashbuckle installation ==
{{warn | It is no longer actively maintained and has been remove from .NET 9 templates. [https://github.com/dotnet/aspnetcore/issues/54599 ]}}
Already installed in .NET 7+
<kode lang='ps'>
<filebox fn='MyWebApi.csproj' lang='xml'>
dotnet add package Swashbuckle.AspNetCore
<ItemGroup>
</kode>
  <PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
</ItemGroup>
</filebox>


<filebox fn='Program.cs'>
<filebox fn='Program.cs'>
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddSwaggerGen();
Ligne 25 : Ligne 22 :
</filebox>
</filebox>


== Installation Old ==
= Installation Old =
<kode lang='bash'>
<kode lang='bash'>
dotnet add package Swashbuckle.AspNetCore
dotnet add package Swashbuckle.AspNetCore
Ligne 54 : Ligne 51 :
</filebox>
</filebox>


== Usage ==
= Usage =
<filebox fn='Controllers/ItemController.cs'>
<filebox fn='Controllers/ItemController.cs'>
[ApiController]
[ApiController]
Ligne 67 : 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] ==
= [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;amp;}}}}
{{warn | {{boxx|&}} has to be escaped as {{boxx|&amp;amp;}}}}
<filebox fn='MyProject.csproj' lang='xml'>
<filebox fn='MyProject.csproj' lang='xml'>
Ligne 114 : Ligne 111 :
</filebox>
</filebox>


== [https://medium.com/@celery_liu/asp-net-core-web-api-with-swagger-api-versioning-for-dotnet-8-c8ce2fd7808c Version] ==
= [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 160 : Ligne 157 :
</filebox>
</filebox>


== Document Filter ==
= Document Filter =
<filebox fn='Program.cs'>
<filebox fn='Program.cs'>
builder.Services.AddSwaggerGen(c =>
builder.Services.AddSwaggerGen(c =>
Ligne 186 : Ligne 183 :
</filebox>
</filebox>


=== [https://vrcode.medium.com/implement-health-check-in-dot-net-core-api-and-integrate-in-swagger-5b812601cb35 Add HealthChecks endpoint] ===
== [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 233 : 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] ==
= [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 330 : 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] ==
= [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

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 &amp;
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

Ouvrir le navigateur sur swagger

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.