Versionierung einer REST-API

1. Das Problem

Die Entwicklung einer REST-API ist ein schwieriges Problem, für das viele Optionen verfügbar sind. Dieser Artikel beschreibt einige dieser Optionen.

2. Was steht im Vertrag?

Vor allem müssen wir eine einfache Frage beantworten: Was ist der Vertrag zwischen der API und dem Client?

2.1. URIs Bestandteil des Vertrags?

Betrachten wir zunächst die URI-Struktur der REST-API - ist das Teil des Vertrags? Sollten Kunden Lesezeichen setzen, Hardcodes erstellen und sich im Allgemeinen auf URIs der API verlassen?

Wenn dies der Fall ist, wird die Interaktion des Kunden mit dem REST-Service nicht mehr vom Service selbst gesteuert, sondern von dem, was Roy Fielding als Out-of-Band- Informationen bezeichnet:

Eine REST-API sollte ohne Vorkenntnisse über den ursprünglichen URI (Lesezeichen) und eine Reihe standardisierter Medientypen hinaus eingegeben werden, die für die beabsichtigte Zielgruppe geeignet sind. Ein Fehler hier impliziert, dass Out-of-Band-Informationen die Interaktion anstelle von Hypertext steuern.

URIs sind also eindeutig nicht Vertragsbestandteil ! Der Client sollte nur einen einzigen URI kennen - den Einstiegspunkt in die API. Alle anderen URIs sollten beim Konsumieren der API erkannt werden.

2.2. Medientypen Bestandteil des Vertrages?

Was ist mit den Medientypinformationen, die für die Darstellung von Ressourcen verwendet werden - sind diese Bestandteil des Vertrags zwischen dem Kunden und dem Service?

Um die API erfolgreich nutzen zu können, muss der Client über Vorkenntnisse dieser Medientypen verfügen . Tatsächlich repräsentiert die Definition dieser Medientypen den gesamten Vertrag.

Daher sollte sich der REST-Service hier am meisten konzentrieren:

Eine REST-API sollte fast den gesamten beschreibenden Aufwand für die Definition der Medientypen verwenden, die zur Darstellung von Ressourcen und zur Steuerung des Anwendungsstatus verwendet werden, oder für die Definition erweiterter Beziehungsnamen und / oder hypertextfähiger Markierungen für vorhandene Standardmedientypen.

Die Medientypdefinitionen sind daher Bestandteil des Vertrags und sollten Vorkenntnisse für den Client sein, der die API verwendet. Hier kommt die Standardisierung ins Spiel.

Wir haben jetzt eine gute Vorstellung davon, was der Vertrag ist. Fahren wir fort mit der tatsächlichen Lösung des Versionsproblems.

3. Optionen auf hoher Ebene

Lassen Sie uns nun die allgemeinen Ansätze zur Versionierung der REST-API diskutieren:

  • URI-Versionierung - Versionieren Sie den URI-Bereich mithilfe von Versionsindikatoren
  • Medientyp-Versionierung - Version der Darstellung der Ressource

Wenn wir die Version im URI-Bereich einführen, werden die Darstellungen von Ressourcen als unveränderlich betrachtet. Wenn also Änderungen in die API eingeführt werden müssen, muss ein neuer URI-Bereich erstellt werden.

Angenommen, eine API veröffentlicht die folgenden Ressourcen - Benutzer und Berechtigungen:

//host/v1/users //host/v1/privileges

Nehmen wir nun an, dass für eine grundlegende Änderung der Benutzer- API die Einführung einer zweiten Version erforderlich ist:

//host/v2/users //host/v2/privileges

Wenn wir den Medientyp versionieren und die Sprache erweitern, führen wir die Inhaltsverhandlung basierend auf diesem Header durch. Die REST-API würde MIME-Medientypen von benutzerdefinierten Anbietern anstelle von generischen Medientypen wie application / json verwenden . Wir werden diese Medientypen anstelle der URIs versionieren.

Zum Beispiel:

===> GET /users/3 HTTP/1.1 Accept: application/vnd.myname.v1+json <=== HTTP/1.1 200 OK Content-Type: application/vnd.myname.v1+json { "user": { "name": "John Smith" } }

Weitere Informationen und Beispiele zu diesem Thema finden Sie in diesem Artikel "Benutzerdefinierte Medientypen für Rest-APIs".

Es ist wichtig zu verstehen, dass der Client keine Annahmen über die Struktur der Antwort macht, die über die im Medientyp definierten Werte hinausgehen.

Aus diesem Grund sind generische Medientypen nicht ideal. Diese bieten nicht genügend semantische Informationen und zwingen den Client zur Verwendung. Sie benötigen zusätzliche Hinweise, um die tatsächliche Darstellung der Ressource zu verarbeiten.

Eine Ausnahme bildet die Verwendung einer anderen Methode zur eindeutigen Identifizierung der Semantik des Inhalts, z. B. eines XML-Schemas.

4. Vor- und Nachteile

Nachdem wir nun ein klares Konzept der Vertragsbestandteile zwischen dem Kunden und dem Service sowie einen allgemeinen Überblick über die Optionen zur Versionierung der API haben, wollen wir die Vor- und Nachteile der einzelnen Ansätze erörtern.

Erstens führt die Einführung von Versionskennungen in den URI zu einem sehr großen URI-Footprint. Dies liegt an der Tatsache, dass jede Änderung in einer der veröffentlichten APIs einen völlig neuen Baum von Darstellungen für die gesamte API einführt. Im Laufe der Zeit wird dies zu einer Belastung für die Wartung und zu einem Problem für den Kunden, der nun mehr Optionen zur Auswahl hat.

Versionskennungen in der URI sind ebenfalls stark unflexibel . Es gibt keine Möglichkeit, die API einer einzelnen Ressource oder eine kleine Teilmenge der gesamten API einfach weiterzuentwickeln.

Wie bereits erwähnt, ist dies ein Alles-oder-Nichts-Ansatz. Wenn ein Teil der API auf die neue Version verschoben wird, muss die gesamte API mitgeschaltet werden. Dies macht auch das Upgrade von Clients von Version 1 auf Version 2 zu einem wichtigen Unterfangen - was zu langsameren Upgrades und viel längeren Verfallszeiten für die alten Versionen führt.

HTTP-Caching ist auch bei der Versionierung ein wichtiges Anliegen.

Aus der Sicht der Proxy-Caches in der Mitte hat jeder Ansatz Vor- und Nachteile. Wenn der URI versioniert ist, muss der Cache mehrere Kopien jeder Ressource aufbewahren - eine für jede Version der API. Dies belastet den Cache und verringert die Cache-Trefferquote, da verschiedene Clients unterschiedliche Versionen verwenden.

Also, some cache invalidation mechanisms will no longer work. If the media type is the one that is versioned, then both the Client and the Service need to support the Vary HTTP header to indicate that there are multiple versions being cached.

From the perspective of client caching however, the solution that versions the media type involves slightly more work than the one where URIs contain the version identifier. This is because it's simply easier to cache something when its key is an URL than a media type.

Let's end this section with defining some goals (straight out of API Evolution):

  • keep compatible changes out of names
  • avoid new major versions
  • makes changes backwards-compatible
  • think about forwards-compatibility

5. Possible Changes to the API

Next, let's consider the types of changes to the REST API – these are introduced here:

  • representation format changes
  • resource changes

5.1. Adding to the Representation of a Resource

The format documentation of the media type should be designed with forward compatibility in mind. Specifically, a client should ignore information that it doesn't understand (which JSON does better than XML).

Now, adding information in the Representation of a resource will not break existing clients if these are correctly implemented.

To continue our earlier example, adding the amount in the representation of the user will not be a breaking change:

{ "user": { "name": "John Smith", "amount": "300" } }

5.2. Removing or Changing an Existing Representation

Removing, renaming or generally restructuring information in the design of existing representations is a breaking change for clients. This is because they already understand and rely on the old format.

This is where Content Negotiation comes in. For such changes, we can add a new vendor MIME media type.

Let's continue with the previous example. Say we want to break the name of the user into firstname and lastname:

===> GET /users/3 HTTP/1.1 Accept: application/vnd.myname.v2+json <=== HTTP/1.1 200 OK Content-Type: application/vnd.myname.v2+json { "user": { "firstname": "John", "lastname": "Smith", "amount": "300" } }

As such, this does represent an incompatible change for the Client – which will have to request the new Representation and understand the new semantics. However, the URI space will remain stable and will not be affected.

5.3. Major Semantic Changes

These are changes in the meaning of the Resources, the relations between them or what the map to in the backend. This kind of changes may require a new media type, or they may require publishing a new, sibling Resource next to the old one and making use of linking to point to it.

While this sounds like using version identifiers in the URI all over again, the important distinction is that the new Resource is published independently of any other Resources in the API and will not fork the entire API at the root.

The REST API should adhere to the HATEOAS constraint. According to this, most of the URIs should be DISCOVERED by Clients, not hardcoded. Changing such an URI should not be considered an incompatible change. The new URI can replace the old one and Clients will be able to re-discover the URI and still function.

It's worth noting however that, while using version identifiers in the URI is problematic for all of these reasons, it is not un-RESTful in any way.

6. Conclusion

This article tried to provide an overview of the very diverse and difficult problem of evolving a REST Service. We discussed the two common solutions, advantages and disadvantages of each one, and ways to reason about these approaches in the context of REST.

Der Artikel schließt mit der Begründung der zweiten Lösung - der Versionierung der Medientypen und der Prüfung möglicher Änderungen an einer RESTful-API.

Die vollständige Implementierung dieses Tutorials finden Sie im GitHub-Projekt.

7. Weiterführende Literatur

Normalerweise sind diese Leseressourcen im gesamten Artikel verlinkt, aber in diesem Fall gibt es einfach zu viele gute:

    • REST-APIs müssen hypertextgesteuert sein
    • API Evolution
    • Verknüpfung für eine HTTP-API
    • Kompatibilitätsstrategien