Dokumentace REST API pomocí OpenAPI a Swagger UI

03.07.2021

Slovníček

REST API Seznam doporučení a pokynů pro návrh API, který sepsal Roy Fielding v roce 2000. REST API se obvykle vztahuje k vytváření webových služeb, které komunikují prostřednictvím HTTP/S protokolu a používají formát JSON pro výměnu dat. Přestože je REST API velmi rozšířené, nejde o standard ani protokol, což znamená, že jeho implementace se mohou lišit.
OpenAPI OpenAPI je specifikace ve formátu YAML nebo JSON určená pro popis REST API, která vychází z původní Swagger specifikace. Specifikace je srozumitelná jak pro vývojáře, tak i pro strojové zpracování. Díky ní lze například generovat dokumentaci nebo ji využít pro automatické generování kódu. Původní Swagger specifikace pochází z roku 2011, zatímco první verze OpenAPI byla vydána v roce 2017.
Swagger UI Swagger UI je v našem případě NuGet balíček, který dokáže na základě OpenAPI specifikace vygenerovat webovou dokumentaci. Vypadat může například takto.
HATEOAS Jedná se o REST API princip, kdy má API posílat ve svých odpovědích i hyperlinky na související endpointy, které může klient použít. V takovém případě můžeme použít například HAL formát, který hyperlinky podporuje, ale zatím se jedná pouze o RFC draft. Záleží zde na požadavcích a je potřeba uplatnit princip YAGNI. REST API bez HATEOAS lze označit jako RESTlike API, zatímco REST API s implementovaným HATEOAS lze považovat za RESTful API.
Verzování Existuje několik přístupů k verzování, jako například použití URI (URL path-based), query parameters, custom headers nebo content negotiation. Každý z těchto přístupů má své výhody i nevýhody.

Obsah

Úvod

Připravíme REST API (bez HATEOAS, s verzováním pomocí URI) a pomocí NuGet balíčků OpenAPI a Swagger UI vytvoříme dokumentaci přístupnou z webu. Tuto dokumentaci si následně může zobrazit kdokoliv, kdo bude s API pracovat.

Příprava API

Vytvoříme si WebAPI projekt. WebAPI šablona ve výchozím nastavení již OpenAPI podporuje a automaticky se nám nainstaluje doplněk Swashbuckle.AspNetCore.

dotnet new webapi
Do startupu se nám přidala následující konfigurace
services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "rest", Version = "v1" });
});
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "rest v1"));

Nyní máme k dispozici výchozí WeatherForecastController. Pokud API spustíme a přejdeme na stránku /swagger/index.html, zobrazí se nám následující interaktivní dokumentace

Dokumentace je připravena k použití, ale ještě si ji vylepšíme 🚀

Přidání podpory pro XML komentáře

Do Swagger dokumentace přidáme naše vlastní komentáře, které si ale musíme nejdříve povolit v Project > Properties > Build: Output. Zároveň potlačíme chybovou hlášku CS1591: Missing XML comment for publicly visible type or member, která by se jinak zobrazovala u většiny veřejných metod a proměnných.

Swagger službě dáme vědět, že si má načítat komentáře z nově generovaného souboru WebApi.xml.

services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "WebApi", Version = "v1" });
    c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "WebApi.xml"));
});

Nyní upravíme náš API endpoint a přidáme komentáře včetně atributu summary.

  • Endpoint obsahuje nový vstupní objekt ForecastHorizontInputModel, který obsahuje proměnnou se summary komentářem a required atributem. Komentář s atributem se v dokumentaci také projeví.
Do XML komentářů nelze vkládat speciální znaky, a pokud je to potřeba, musíme je nahradit.

/// <summary>
/// Get temperature in Celsius for each date.
/// </summary>
/// URL request example&#x003A; 
/// 
///     /weatherforecast?dates=2020-05-01&amp;dates=2020-05-02
///     
[HttpGet]
public IActionResult Get([FromQuery]ForecastHorizontInputModel model)
{
    // ...
    return Ok(result);
}
public class ForecastHorizontInputModel
{
    /// <summary>Collection of dates. Each date can be between 2020-01-01 and five days in advance.</summary>
    [Required]
    public List<DateTime> Dates { get; set; }
}

Rozdělení dokumentace na více stránek

Pokud naše API obsahuje více endpointů, může být přehlednější rozdělit OpenAPI dokumentaci na více stránek. Toho dosáhneme pomocí atributu ApiExplorerSettings a úpravy ve startup.cs. Jako příklad přidáme nový EarthquakeAlertController, který bude mít samostatnou Swagger UI stránku.

// WeatherForecastController.cs
[ApiController] 
[Route("[controller]")]
[ApiExplorerSettings(GroupName = "weather")]
public class WeatherForecastController : ControllerBase
// EarthquakeAlertController.cs
[ApiController]
[Route("[controller]")]
[ApiExplorerSettings(GroupName = "earthquake")]
public class EarthquakeAlertController : ControllerBase
Startup.cs
// ConfigureServices
services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("weather", new OpenApiInfo { Title = "Weather Forecast", Version = "v1" });
    c.SwaggerDoc("earthquake", new OpenApiInfo { Title = "Earthquake Alert", Version = "v1" });
    c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "WebApi.xml"));
});
// Configure
app.UseSwagger();
app.UseSwaggerUI(c =>
{
    c.SwaggerEndpoint("/swagger/weather/swagger.json", "Weather API");
    c.SwaggerEndpoint("/swagger/earthquake/swagger.json", "Earthquake API");
});

Group name, Swagger Doc name a Swagger Endpoint name v cestě se musí rovnat. Mezi kontrolerama pak můžeme v dokumentaci snadno přepínat.

Verzování

Naše API se může vyvíjet a může nastat potřeba implementovat tzv. breaking změnu, která by mohla znefunkčnit případného API klienta (například zrušení endpointu, změna vstupního parametru, změna odpovědi atd.). Pokud klient není pod naší správou, měli bychom mít pro takové případy připravenou verzovací strategii. Verzovacích strategií je několik a každá má své výhody i nevýhody.

V tomto případě budu specifikovat verzi přímo v URL adrese (URI neboli také URL path-based verzování). Dokumentace bude rozdělena podle jednotlivých verzí, nikoliv nutně podle jednotlivých kontrolerů, jak jsme to nastavili výše.

Nastavení

Vytvoříme si nový projekt

dotnet new demoapi

Přidáme podporu pro verzování v podobě NuGet balíčku Microsoft.AspNetCore.Mvc.Versioning, který zaregistrujeme jako middleware ve Startup.cs.

dotnet add package Microsoft.AspNetCore.Mvc.Versioning
public void ConfigureServices(IServiceCollection services)
{
  //...
  services.AddApiVersioning(options =>
  {
    // Add response headers "api-supported-versions" and "api-deprecated-versions"
    options.ReportApiVersions = true;
  });

  services.AddVersionedApiExplorer(options =>
  {
    // Setup format such as v1.0
    options.GroupNameFormat = "'v'VV";

    // Set to true when URL path based versioning is in use
    options.SubstituteApiVersionInUrl = true;
  });
  //...
}
Zkopírujeme WeatherForecastController.cs a rozdělíme API na dvě verze
[ApiController]
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/WeatherForecast")]
public class WeatherForecastController : ControllerBase
[ApiController]
[ApiVersion("2.0")]
[Route("api/v{version:apiVersion}/WeatherForecast")]
public class WeatherForecastV2Controller : ControllerBase

Při vytváření nové verze není potřeba duplikovat všechny metody (tedy i ty, které se nezměnily), ale lze použít atribut MapToApiVersion, pomocí kterého si můžeme nadefinovat, které metody jsou součástí, kterých API verzí. Konkrétní metoda tedy může být součástí předchozí i nové verze. Nyní nám budou fungovat následující odkazy:

  • /api/v1.0/WeatherForecast
  • /api/v2.0/WeatherForecast

Promítnutí do dokumentace

O verzování musíme informovat knihovny Swagger a Swagger UI. Pro tento účel jsem si vypůjčil třídy z následujícího blogu, na který jsem při psaní tohoto článku narazil, a trochu je upravil. Vytvoříme tedy následující třídy: