Web API: Versioning

3 Minuten zum Lesen
Web API: Versioning
Bei Änderungen an einer API, die nicht rückwärtskompatibel sind, ist die Versionierung entscheidend, um eine reibungslose Kommunikation mit unterschiedlichen Client-Versionen zu gewährleisten. Eine durchdachte Implementierung hilft, Downtimes zu vermeiden und die Migration schrittweise zu gestalten.

Die Entwicklung von Web-APIs bringt häufig Herausforderungen mit sich, insbesondere wenn es zu Änderungen in der Struktur kommt. Modifikationen an Request-Parametern, Responses oder Routen können zu Problemen führen, wenn die Clients, die die API nutzen, nicht zeitgleich angepasst werden können. Um sicherzustellen, dass die Auswirkungen für die Nutzenden minimal bleiben, ist es entscheidend, Änderungen klar zu kommunizieren. Idealerweise bleibt jede API-Änderung rückwärtskompatibel, doch Breaking Changes sind oft unvermeidlich. Es ist ratsam, bereits in der Planungsphase über mögliche Versionierungen nachzudenken, um spätere Anpassungen zu erleichtern. Aber auch später ist es möglich, eine Versionierung zu implementieren, ohne viel Aufwand zu betreiben.

Einführung der API-Versionierung

Hier kommt die API-Versionierung ins Spiel. Durch die Einführung einer Versionierung können ältere Clients weiterhin mit der bestehenden API arbeiten, während neue Clients auf die aktualisierte Version zugreifen. Dies erleichtert eine schrittweise Migration, bevor die alte API-Version schließlich stillgelegt werden kann. Obwohl es zunächst komplex erscheinen mag, bieten Tools wie ASP.NET Core von Haus aus Unterstützung für API-Versionierung über NuGet-Pakete.
Wer erst zu einem späteren Zeitpunkt eine API-Versionierung einführt, kann für bestehende Routen auch eine Default-Version festlegen, um die bestehenden Clients nicht zu beeinträchtigen.

Übermittlung der Versionsinformationen

Ein zentraler Aspekt der Versionierung ist die Methode der Übermittlung der Versionsinformationen zwischen Client und API. Es gibt verschiedene Ansätze, wie zum Beispiel die Übergabe über Query-Parameter, als Teil der Route oder im Accept-Header. Die Nutzung des Accept-Headers wird häufig empfohlen, da er eine native Möglichkeit darstellt, mit der Clients angeben können, in welchem Format und welcher API-Version sie kommunizieren möchten. Dabei ist es wichtig, das Caching zu berücksichtigen, um sicherzustellen, dass keine veralteten Informationen zurückgegeben werden.

serviceCollection.AddApiVersioning(options =>
{
    options.ApiVersionReader = ApiVersionReader.Combine(
        new QueryStringApiVersionReader(), // ?api-version=1.0
        new MediaTypeApiVersionReader() // Content-Type: application/json;v=1.0
    );
});

Strategien zur Versionsstruktur im Code

Ein weiterer Punkt sind die strategischen Überlegungen zur Versionsstruktur im Code. Es gibt verschiedene Ansätze zur Trennung der API-Versionen, die von der Duplizierung der Controller bis zur Verwendung von Vererbung oder der Auslagerung in separate Klassenbibliotheken reichen. Die gewählte Methode sollte sich am Umfang der Änderungen orientieren. Bei kleineren Anpassungen an wenigen Routen kann eine separate Klasse ausreichend sein, während bei umfangreichen Änderungen Klassenbibliotheken mit eigenen Namespaces sinnvoller sein können.

[ApiController]
[ApiVersion("2.0")]
[Route("api/[controller]")]
public class WeatherForecastV2Controller : ControllerBase
{
    [HttpGet]
    public IEnumerable<WeatherForecast> Get()
    {
        // do something
    }
}

Kommunikation mit den Clients

Sind die API-Versionen einmal festgelegt, ist die Kommunikation mit den Clients von entscheidender Bedeutung. Wenn du OpenAPI zur Dokumentation nutzt, kannst du für jede Version eine eigene Spezifikation erstellen. Dies ermöglicht es den Clients, sich auf die entsprechenden API-Versionen zu beziehen. Bei verschiedenen Clients, die unterschiedliche API-Versionen verwenden, stellt sich die Frage, wie die Version zwischen Client und API ausgehandelt wird. Diese ,Version Negotiation” beschreibt die minimalen und maximalen API-Versionen, die beide Parteien unterstützen müssen, um einen gemeinsamen Nenner zu finden. Das ASP.NET Core API-Versioning-Paket bietet hierfür auch eine Client-Implementierung, um die richtige API-Version zu ermitteln, aber auch um neue API-Versionen zu erkennen:

services.AddHttpClient(
    "SomeApi",
    client => client.BaseAddress = new Uri("https://some.api.com/"))
.AddApiVersion( 1.0, new QueryApiVersionWriter() );

Ein finales Beispiel könnt ihr dem Open Space Planner entnehmen.

Wie handhabst du API-Versionierung und ist deine API von Anfang an versioniert? Schau gerne bei uns auf LinkedIn vorbei und diskutiere mit.

Autoren

Florian Bader

Florian Bader

Florian ist Solution Architect und Microsoft Most Valuable Professional für Azure IoT mit langjähriger Erfahrung im Bereich DevOps, Cloud und Digitalisierung. Er unterstützt Unternehmen dabei, effiziente und effektive Lösungen zu entwickeln, die ihre digitalen Projekte nachhaltig zum Erfolg führen.