Best Practices für die API-Versionierung? [geschlossen]

877

Gibt es bekannte Anleitungen oder Best Practices für die REST-API-Versionierung von Webdiensten?

Ich habe festgestellt, dass AWS die Versionierung über die URL des Endpunkts durchführt . Ist dies der einzige Weg oder gibt es andere Wege, um dasselbe Ziel zu erreichen? Wenn es mehrere Wege gibt, was sind die Vorzüge jedes Weges?

Swaroop CH
quelle

Antworten:

682

Dies ist eine gute und knifflige Frage. Das Thema URI-Design ist gleichzeitig der wichtigste Teil einer REST-API und daher ein potenziell langfristiges Engagement gegenüber den Benutzern dieser API .

Da die Entwicklung einer Anwendung und in geringerem Maße ihrer API eine Tatsache des Lebens ist und sogar der Entwicklung eines scheinbar komplexen Produkts wie einer Programmiersprache ähnelt, sollte das URI-Design weniger natürliche Einschränkungen aufweisen und beibehalten werden im Laufe der Zeit . Je länger die Lebensdauer der Anwendung und der API ist, desto größer ist das Engagement für die Benutzer der Anwendung und der API.

Andererseits ist eine andere Tatsache im Leben, dass es schwierig ist, alle Ressourcen und ihre Aspekte vorherzusehen, die durch die API verbraucht würden. Glücklicherweise ist es nicht erforderlich, die gesamte API zu entwerfen, die bis zur Apokalypse verwendet wird . Es reicht aus, alle Ressourcenendpunkte und das Adressierungsschema jeder Ressource und Ressourceninstanz korrekt zu definieren.

Im Laufe der Zeit müssen Sie möglicherweise neue Ressourcen und neue Attribute zu jeder bestimmten Ressource hinzufügen. Die Methode, mit der API-Benutzer auf eine bestimmte Ressource zugreifen, sollte sich jedoch nicht ändern, sobald ein Ressourcenadressierungsschema öffentlich und damit endgültig wird.

Diese Methode gilt für die HTTP-Verbsemantik (z. B. sollte PUT immer aktualisiert / ersetzt werden) und für HTTP-Statuscodes, die in früheren API-Versionen unterstützt werden (sie sollten weiterhin funktionieren, damit API-Clients, die ohne menschliches Eingreifen gearbeitet haben, weiterhin funktionieren können so wie das).

Da die Einbettung der API-Version in die URI das Konzept von Hypermedia als Motor des Anwendungsstatus (in der Dissertation von Roy T. Fieldings angegeben) durch eine Ressourcenadresse / URI stören würde, die sich im Laufe der Zeit ändern würde, würde ich diese API schließen Versionen sollten nicht lange in Ressourcen-URIs aufbewahrt werden. Dies bedeutet, dass Ressourcen-URIs, auf die sich API-Benutzer verlassen können, Permalinks sein sollten .

Sicher, es ist möglich, die API-Version in den Basis-URI einzubetten, aber nur für vernünftige und eingeschränkte Zwecke wie das Debuggen eines API-Clients , der mit der neuen API-Version funktioniert. Solche versionierten APIs sollten zeitlich begrenzt sein und nur begrenzten Gruppen von API-Benutzern (wie bei geschlossenen Betas) zur Verfügung stehen. Ansonsten verpflichten Sie sich, wo Sie nicht sollten.

Einige Gedanken zur Wartung von API-Versionen mit Ablaufdatum. Alle Programmierplattformen / -sprachen, die üblicherweise zur Implementierung von Webdiensten verwendet werden (Java, .NET, PHP, Perl, Rails usw.), ermöglichen die einfache Bindung von Webdienstendpunkten an einen Basis-URI. Auf diese Weise ist es einfach , eine Sammlung von Dateien / Klassen / Methoden zu sammeln und getrennt von verschiedenen API-Versionen zu halten .

Mit dem POV der API-Benutzer ist es auch einfacher, mit einer bestimmten API-Version zu arbeiten und diese zu binden, wenn dies offensichtlich ist, jedoch nur für eine begrenzte Zeit, dh während der Entwicklung.

Mit dem POV des API-Betreuers ist es einfacher, verschiedene API-Versionen parallel zu verwalten, indem Quellcodeverwaltungssysteme verwendet werden, die vorwiegend mit Dateien als kleinste Einheit der (Quellcode-) Versionierung arbeiten.

Bei API-Versionen, die in URI deutlich sichtbar sind, gibt es jedoch eine Einschränkung: Man könnte diesen Ansatz auch ablehnen, da der API-Verlauf im URI-Design sichtbar / aparent wird und daher im Laufe der Zeit Änderungen unterliegt, die gegen die Richtlinien von REST verstoßen. Genau!

Um diesen vernünftigen Einwand zu umgehen, müssen Sie die neueste API-Version unter dem versionlosen API-Basis-URI implementieren. In diesem Fall können API-Client-Entwickler Folgendes auswählen:

  • Entwickeln Sie gegen die neueste Version (verpflichten Sie sich, die Anwendung so zu warten, dass sie vor eventuellen API-Änderungen geschützt ist, die den schlecht gestalteten API-Client beschädigen könnten ).

  • an eine bestimmte Version der API binden (was offensichtlich wird), jedoch nur für eine begrenzte Zeit

Wenn API v3.0 beispielsweise die neueste API-Version ist, sollten die folgenden beiden Aliase sein (dh sich für alle API-Anforderungen identisch verhalten):

http: // shonzilla / api / customers / 1234 
http: // shonzilla / api /v3.0 / customers / 1234
http: // shonzilla / api / v3 / customers / 1234

Darüber hinaus sollten API-Clients, die weiterhin versuchen, auf die alte API zu verweisen, über die Verwendung der neuesten vorherigen API-Version informiert werden, wenn die von ihnen verwendete API-Version veraltet ist oder nicht mehr unterstützt wird . Zugriff auf eine der veralteten URIs wie diese:

http: // shonzilla / api /v2.2 / customers / 1234
http: // shonzilla / api /v2.0 / customers / 1234
http: // shonzilla / api / v2 / customers / 1234
http: // shonzilla / api /v1.1 / customers / 1234
http: // shonzilla / api / v1 / customers / 1234

sollte einen der 30x HTTP-Statuscodes zurückgeben, die die Umleitung angeben , die in Verbindung mit dem LocationHTTP-Header verwendet wird, der zur entsprechenden Version des Ressourcen-URI umleitet, die noch diese sein muss:

http: // shonzilla / api / customers / 1234

Es gibt mindestens zwei HTTP-Statuscodes für die Umleitung, die für API-Versionsszenarien geeignet sind:

  • 301 Wird permanent verschoben, um anzuzeigen , dass die Ressource mit einem angeforderten URI dauerhaft in einen anderen URI verschoben wird (dies sollte ein Permalink für eine Ressourceninstanz sein, der keine API-Versionsinformationen enthält). Dieser Statuscode kann verwendet werden, um eine veraltete / nicht unterstützte API-Version anzuzeigen und den API-Client darüber zu informieren, dass ein versionierter Ressourcen-URI durch einen Ressourcen-Permalink ersetzt wurde .

  • 302 Gefunden , um anzuzeigen , dass sich die angeforderte Ressource vorübergehend an einem anderen Speicherort befindet, während der angeforderte URI möglicherweise noch unterstützt wird. Dieser Statuscode kann nützlich sein, wenn die URIs ohne Version vorübergehend nicht verfügbar sind und eine Anforderung unter Verwendung der Umleitungsadresse wiederholt werden sollte (z. B. Verweisen auf den URI mit eingebetteter APi-Version), und wir möchten Clients anweisen, ihn weiterhin zu verwenden (dh die Permalinks).

  • Weitere Szenarien finden Sie im Kapitel Redirection 3xx der HTTP 1.1-Spezifikation

Shonzilla
quelle
142
Die Verwendung einer Versionsnummer in der URL sollte nicht als schlechte Praxis angesehen werden, wenn sich die zugrunde liegende Implementierung ändert. "Wenn sich die Schnittstelle zu einem Dienst nicht abwärtskompatibel ändert, wurde in Wirklichkeit ein völlig neuer Dienst erstellt ... Aus Sicht des Kunden ist ein Dienst nur eine Schnittstelle und einige nicht funktionale Eigenschaften. Wenn sich die Schnittstelle zu einem Dienst nicht abwärtskompatibel ändert, stellt sie keine Instanz des ursprünglichen Dienstes mehr dar, sondern ist ein völlig neuer Dienst. " ibm.com/developerworks/webservices/library/ws-version
benvolioT
7
Haben Sie Gedanken darüber, einen Header mit der Versionsnummer hinzuzufügen, damit dieser von Clients oder Entwicklern überprüft werden kann?
Webclimber
11
Siehe auch die Verwendung eines Accept-Headers, um die vom Client erwartete Version anzugeben: blog.steveklabnik.com/2011/07/03/…
Weston Ruter
52
Zum letzten Teil: Ich würde sagen, dass eine API, die veraltet ist und nicht mehr unterstützt wird, zurückgegeben werden sollte 410 Gone, da eine Umleitung möglicherweise darauf hinweist, dass der neue Speicherort kompatibel ist, wenn dies nicht der Fall ist. Wenn die API lediglich veraltet ist, aber noch vorhanden ist, kann ein WarningHTTP-Header in der Antwort eine Option sein.
Michael Stum
22
Wie gehen Sie mit Clients um, die bereits die stabile URL wie shonzilla / api / customers / 1234 verwenden und auf eine neue Version aktualisieren möchten? Wie können Sie sie zwingen, die V2 (die alte) zur URL hinzuzufügen?
Dejell
273

Die URL sollte NICHT die Versionen enthalten. Die Version hat nichts mit der "Idee" der Ressource zu tun, die Sie anfordern. Sie sollten versuchen, sich die URL als Pfad zum gewünschten Konzept vorzustellen - nicht als Rückgabe des Elements. Die Version schreibt die Darstellung des Objekts vor, nicht das Konzept des Objekts. Wie bereits auf anderen Postern erwähnt, sollten Sie das Format (einschließlich der Version) im Anforderungsheader angeben.

Wenn Sie sich die vollständige HTTP-Anforderung für die URLs mit Versionen ansehen, sieht dies folgendermaßen aus:

(BAD WAY TO DO IT):

http://company.com/api/v3.0/customer/123
====>
GET v3.0/customer/123 HTTP/1.1
Accept: application/xml

<====
HTTP/1.1 200 OK
Content-Type: application/xml
<customer version="3.0">
  <name>Neil Armstrong</name>
</customer>

Die Kopfzeile enthält die Zeile, die die gewünschte Darstellung enthält ("Accept: application / xml"). Dort sollte die Version hingehen. Jeder scheint die Tatsache zu beschönigen, dass Sie möglicherweise dasselbe in verschiedenen Formaten möchten und dass der Kunde in der Lage sein sollte, nach dem zu fragen, was er möchte. Im obigen Beispiel fordert der Client JEDE XML-Darstellung der Ressource an - nicht wirklich die wahre Darstellung dessen, was er will. Der Server könnte theoretisch etwas zurückgeben, das nichts mit der Anforderung zu tun hat, solange es sich um XML handelt, und es müsste analysiert werden, um zu erkennen, dass es falsch ist.

Ein besserer Weg ist:

(GOOD WAY TO DO IT)

http://company.com/api/customer/123
===>
GET /customer/123 HTTP/1.1
Accept: application/vnd.company.myapp.customer-v3+xml

<===
HTTP/1.1 200 OK
Content-Type: application/vnd.company.myapp-v3+xml
<customer>
  <name>Neil Armstrong</name>
</customer>

Nehmen wir weiter an, die Clients halten das XML für zu ausführlich und möchten jetzt stattdessen JSON. In den anderen Beispielen müssten Sie eine neue URL für denselben Kunden haben, damit Sie am Ende Folgendes erhalten:

(BAD)
http://company.com/api/JSONv3.0/customers/123
  or
http://company.com/api/v3.0/customers/123?format="JSON"

(oder etwas ähnliches). Tatsächlich enthält jede HTTP-Anforderung das gesuchte Format:

(GOOD WAY TO DO IT)
===>
GET /customer/123 HTTP/1.1
Accept: application/vnd.company.myapp.customer-v3+json

<===
HTTP/1.1 200 OK
Content-Type: application/vnd.company.myapp-v3+json

{"customer":
  {"name":"Neil Armstrong"}
}

Mit dieser Methode haben Sie viel mehr Gestaltungsfreiheit und halten tatsächlich an der ursprünglichen Idee von REST fest. Sie können Versionen ändern, ohne Clients zu stören, oder Clients schrittweise ändern, wenn die APIs geändert werden. Wenn Sie die Unterstützung einer Darstellung beenden möchten, können Sie auf die Anforderungen mit HTTP-Statuscode oder benutzerdefinierten Codes antworten. Der Client kann auch überprüfen, ob die Antwort im richtigen Format vorliegt, und das XML validieren.

Es gibt viele andere Vorteile und ich diskutiere einige davon hier in meinem Blog: http://thereisnorightway.blogspot.com/2011/02/versioning-and-types-in-resthttp-api.html

Ein letztes Beispiel, um zu zeigen, wie schlecht das Einfügen der Version in die URL ist. Nehmen wir an, Sie möchten Informationen innerhalb des Objekts und haben Ihre verschiedenen Objekte versioniert (Kunden sind v3.0, Bestellungen sind v2.0 und shipto object ist v4.2). Hier ist die böse URL, die Sie im Client angeben müssen:

(Another reason why version in the URL sucks)
http://company.com/api/v3.0/customer/123/v2.0/orders/4321/
jeremyh
quelle
10
Der Umgang mit unabhängigen Datenvertragsversionen und Servicevertragsversionen im Accept-Header scheint ebenso chaotisch wie in der URL. Gibt es noch andere Möglichkeiten? Auch wenn ich mehrere Endpunkte habe (Seife, Ruhe), sollte dies auch in Akzeptieren angegeben werden und der Routingdienst am Serverende die Richtung zum richtigen Endpunkt bestimmen lassen ODER ist es akzeptabel, den Endpunkt in der URL codieren zu lassen?
Ideenbrunnen
117
Ich kann dem nicht zustimmen, zumindest nicht bis zu Ihrem letzten Grund. Dies scheint zu sagen, dass die verschiedenen Teile der URI unterschiedliche Versionen haben. Aber darum geht es bei einer API-Version nicht. Es geht darum, EINE Version für die GESAMTE Ressource zu haben. Wenn Sie Versionen ändern, handelt es sich um eine andere API-Ressource. Aus diesem Grund ist es nicht sinnvoll, company.com/api/v3.0/customer/123/v2.0/orders/4321 zu sehen , sondern company.com/api/v3.0/customer/123/orders/4321 Sie versionieren keinen bestimmten Teil der Ressource, sondern die gesamte Ressource.
narr4jesus
90
Die semmantische Verwendung der Versionsnummer im Header scheint besser zu sein. Die Verwendung der URL ist jedoch weitaus praktischer: weniger fehleranfällig, am besten debuggt, von Entwicklern leicht zu erkennen und in Rest-Test-Clients leicht zu ändern.
Daniel Cerecedo
7
Ich denke, das BAD / GOOD-Over vereinfacht die Frage. API steht für "Application Programming Interface" und Versionierungsschnittstellen scheinen eine sehr gute Idee zu sein. Bei APIs geht es nicht nur darum, Ressourcen bereitzustellen. Was getrennt werden muss, ist, dass einige Leute über Schnittstellen sprechen und andere über Ressourcen. Wenn Sie sich die Google Maps-API auf der Registerkarte "Netzwerk" genau ansehen, werden Sie feststellen, dass sie die API-Versionsnummer in der URL enthält. Beispiel: maps.google.com/maps/api/jsv2 während der Authentifizierung. Das jsv2 ist die API-Nummer.
Tom Gruner
6
@Gili: Eigentlich solltest du nicht mehr verwenden, -xda es von RFC6648 veraltet ist .
Jonathan W
98

Wir fanden es praktisch und nützlich, die Version in die URL einzufügen. Auf einen Blick können Sie leicht erkennen, was Sie verwenden. Wir verwenden Alias ​​/ foo bis / foo / (neueste Versionen), um die Verwendung zu vereinfachen, kürzere / sauberere URLs usw., wie aus der akzeptierten Antwort hervorgeht.

Die Abwärtskompatibilität für immer aufrechtzuerhalten, ist oft unerschwinglich und / oder sehr schwierig. Wir bevorzugen eine frühzeitige Benachrichtigung über die Ablehnung, Weiterleitungen wie hier vorgeschlagen, Dokumente und andere Mechanismen.

Yoav Shapira
quelle
5
Die akzeptierte Antwort kann die richtige und reinste sein. Für den Entwickler und alltäglichen Benutzer von APIs ist dies jedoch sicherlich am einfachsten zu verwenden und einzurichten. Der pragmatischste Ansatz. Wie von anderen Google und Amazon angegeben, wird dieser Ansatz ebenfalls verwendet.
Muhammad Rehan Saeed
46

Ich bin damit einverstanden, dass die Versionierung der Ressourcendarstellung besser dem REST-Ansatz folgt ... Ein großes Problem bei benutzerdefinierten MIME-Typen (oder MIME-Typen, die einen Versionsparameter anhängen) ist jedoch die schlechte Unterstützung für das Schreiben in Accept- und Content-Type-Header in HTML und JavaScript.

Zum Beispiel ist es IMO nicht möglich, mit den folgenden Headern in HTML5-Formularen zu POSTEN, um eine Ressource zu erstellen:

Accept: application/vnd.company.myapp-v3+json
Content-Type: application/vnd.company.myapp-v3+json 

Dies liegt daran , das HTML5 - enctypeAttribut eine Aufzählung ist also etwas anderes als die üblichen application/x-www-formurlencoded, multipart/form-dataund text/plainsind ungültig.

... und ich bin mir auch nicht sicher, ob es von allen Browsern in HTML4 unterstützt wird (das ein laxeres Encytpe-Attribut hat, aber ein Problem bei der Browserimplementierung wäre, ob der MIME-Typ weitergeleitet wurde).

Aus diesem Grund denke ich, dass der beste Weg zur Version über den URI ist, aber ich akzeptiere, dass dies nicht der "richtige" Weg ist.

Kevsy
quelle
14
Unter der Annahme, dass die Versionierung in den Headern definiert wurde, könnte man sagen, dass HTML-Formulare, die die native Formularübermittlung verwenden, immer die neueste Version der API verwenden, da sie nicht die spezifische Version übergeben würden, die sie einhalten möchten. Mit XHR-Anforderungen können Sie jedoch tatsächlich die Akzeptierungen ändern und die Header vom Inhaltstyp lesen. Grundformen sind also wirklich das einzige Problem.
Kyle Hayes
Ich bin mir nicht sicher, ob ich der Meinung bin, dass URI am besten geeignet ist, aber die Tatsache, dass Content-Type nicht mit Formularen funktioniert, ist in der Tat sehr wichtig.
wprl
2
@Kyle, ich habe irgendwo in einem Blog gesehen, dass es am besten ist, mit der ersten API-Version zurückzukehren, nicht mit der neuesten, um die beste Kompatibilität zu erzielen, wenn Sie keine Version im Anforderungsheader angeben.
Andy
Das macht für mich jetzt, wo ich darüber nachdenke, sehr viel Sinn.
Kyle Hayes
@KyleHayes vergiss iframes, video / embedded und andere Tags vom Typ "src / href" nicht.
Bitte
21

Fügen Sie Ihre Version in die URI ein. Eine Version einer API unterstützt nicht immer die Typen einer anderen, daher ist das Argument, dass Ressourcen lediglich von einer Version zu einer anderen migriert werden, einfach falsch. Es ist nicht dasselbe wie das Wechseln des Formats von XML zu JSON. Die Typen sind möglicherweise nicht vorhanden oder haben sich semantisch geändert.

Versionen sind Teil der Ressourcenadresse. Sie routen von einer API zu einer anderen. Es ist nicht RESTful, die Adressierung im Header zu verbergen.

Sean O'Dell
quelle
13

Es gibt einige Stellen, an denen Sie eine Versionierung in einer REST-API durchführen können:

  1. Wie bereits erwähnt, in der URI. Dies kann nachvollziehbar und sogar ästhetisch ansprechend sein, wenn Weiterleitungen und dergleichen gut verwendet werden.

  2. Im Header Accepts: befindet sich die Version im Dateityp. Wie 'mp3' vs 'mp4'. Dies wird auch funktionieren, obwohl IMO es ein bisschen weniger gut funktioniert als ...

  3. In der Ressource selbst. In vielen Dateiformaten sind die Versionsnummern eingebettet, normalerweise im Header. Auf diese Weise kann neuere Software "nur funktionieren", indem alle vorhandenen Versionen des Dateityps verstanden werden, während ältere Software stechen kann, wenn eine nicht unterstützte (neuere) Version angegeben wird. Im Kontext einer REST-API bedeutet dies, dass sich Ihre URIs niemals ändern müssen, sondern nur Ihre Antwort auf die bestimmte Version der Daten, die Sie erhalten haben.

Ich kann Gründe sehen, alle drei Ansätze zu verwenden:

  1. Wenn Sie neue APIs "Clean Sweep" durchführen möchten oder wenn Sie größere Versionsänderungen wünschen, bei denen Sie einen solchen Ansatz wünschen.
  2. Wenn Sie möchten, dass der Client weiß, bevor er einen PUT / POST durchführt, ob er funktionieren wird oder nicht.
  3. Wenn es in Ordnung ist, wenn der Client seinen PUT / POST ausführen muss, um herauszufinden, ob es funktionieren wird.
pjz
quelle
8

Die Versionierung Ihrer REST-API erfolgt analog zur Versionierung einer anderen API. Kleinere Änderungen können vorgenommen werden, größere Änderungen erfordern möglicherweise eine völlig neue API. Am einfachsten ist es, jedes Mal von vorne zu beginnen. Dies ist am sinnvollsten, wenn Sie die Version in die URL einfügen. Wenn Sie dem Client das Leben erleichtern möchten, versuchen Sie, die Abwärtskompatibilität aufrechtzuerhalten. Dies können Sie mit Verfall (permanente Umleitung), Ressourcen in mehreren Versionen usw. tun. Dies ist umständlicher und erfordert mehr Aufwand. Aber es ist auch das, was REST in "Coole URIs ändern sich nicht" fördert.

Am Ende ist es genau wie bei jedem anderen API-Design. Wägen Sie den Aufwand gegen die Bequemlichkeit des Kunden ab. Erwägen Sie die Einführung einer semantischen Versionierung für Ihre API, um Ihren Kunden zu verdeutlichen, wie abwärtskompatibel Ihre neue Version ist.

Alexander Torstling
quelle