So versionieren Sie REST-URIs

111

Was ist der beste Weg, um REST-URIs zu versionieren? Derzeit haben wir eine Version # in der URI selbst, dh.

http://example.com/users/v4/1234/

für Version 4 dieser Darstellung.

Gehört die Version zum queryString? dh.

http://example.com/users/1234?version=4

Oder wird die Versionierung am besten auf eine andere Weise durchgeführt?

Mike Pone
quelle
Mögliches Duplikat der Best Practices für die API-Versionierung?
Helen

Antworten:

34

Ich würde sagen, dass es am besten ist, es Teil des URI selbst zu machen (Option 1), da v4 eine andere Ressource als v3 identifiziert. Abfrageparameter wie in Ihrer zweiten Option können am besten verwendet werden, um zusätzliche (Abfrage-) Informationen zur Anforderung und nicht zur Ressource zu übergeben .

Zef Hemel
quelle
11
Die Frage ist, ist es eine andere Ressource, über die wir diskutieren? Oder eine andere Darstellung dieser Ressource? Unterscheidet REST zwischen der Darstellung und der Ressource?
Cheeso
1
@Cheeso - Das OP zeigt an, dass es sich eher um eine andere Darstellung als um eine andere Ressource handelt, daher meine Antwort.
Greg Beech
Dies wurde ausführlicher beantwortet, bevor hier stackoverflow.com/q/389169/104261
Taras Alenin
+1 für "Abfrageparameter wie in Ihrer zweiten Option können am besten verwendet werden, um zusätzliche (Abfrage-) Informationen zur Anfrage und nicht zur Ressource
einzugeben
Ich denke, Sie sollten für verschiedene Darstellungen Header wie "Akzeptieren" verwenden, dann kann der Client dem Server "Ich akzeptiere nur Version 4" angeben und der Server kann mit dieser Darstellung antworten. Wenn keine Annahme gesendet wird, wird die letzte Version bereitgestellt.
Carlos Verdes
190

Versions-URLs nicht, weil ...

  • Sie brechen Permalinks
  • Die URL-Änderungen verbreiten sich wie eine Krankheit über Ihre Benutzeroberfläche. Was machen Sie mit Darstellungen, die sich nicht geändert haben, aber auf die Darstellung verweisen, die sich geändert hat? Wenn Sie die URL ändern, brechen Sie alte Clients. Wenn Sie die URL verlassen, funktionieren Ihre neuen Clients möglicherweise nicht.
  • Die Versionierung von Medientypen ist eine viel flexiblere Lösung.

Angenommen, Ihre Ressource gibt eine Variante von application / vnd.yourcompany.user + xml zurück. Sie müssen lediglich Unterstützung für einen neuen Medientyp application / vnd.yourcompany.userV2 + xml erstellen und durch die Magie der Inhaltsverhandlung Ihre v1 und v2-Clients können friedlich nebeneinander existieren.

In einer RESTful-Schnittstelle ist die Definition der Medientypen, die zwischen dem Client und dem Server ausgetauscht werden, am nächsten an einem Vertrag.

Die URLs, die der Client für die Interaktion mit dem Server verwendet, sollten vom Server bereitgestellt werden, der in zuvor abgerufene Darstellungen eingebettet ist. Die einzige URL, die dem Client bekannt sein muss, ist die Stamm-URL der Schnittstelle. Das Hinzufügen von Versionsnummern zu URLs hat nur dann einen Wert, wenn Sie URLs auf dem Client erstellen, was mit einer RESTful-Schnittstelle nicht zu tun ist.

Wenn Sie Änderungen an Ihren Medientypen vornehmen müssen, die Ihre bestehenden Kunden beschädigen, erstellen Sie eine neue und lassen Sie Ihre URLs in Ruhe!

Und für diejenigen Leser, die derzeit sagen, dass dies keinen Sinn macht, wenn ich application / xml und application / json als Medientypen verwende. Wie sollen wir diese versionieren? Du bist nicht. Diese Medientypen sind für eine RESTful-Oberfläche so gut wie nutzlos, es sei denn, Sie analysieren sie mithilfe von Code-Download. An diesem Punkt ist die Versionierung ein strittiger Punkt.

Darrel Miller
quelle
66
Um die Aufzählungspunkte anzusprechen. 1. Sie unterbrechen keine dauerhaften Links, da Permalinks auf eine bestimmte Version verweisen. 2. Wenn alles versioniert ist, ist dies kein Problem. Alte URLs können immer noch funktionieren. Im Idealfall möchten Sie nicht, dass eine URL der Version 4 eine Zuordnung zu einer Ressource der Version 3 zurückgibt. 3. Vielleicht
Mike Pone
10
Stellen Sie sich vor, beim Upgrade auf eine neue Version eines Webbrowsers sind alle Ihre mit Lesezeichen versehenen Favoriten kaputt gegangen! Denken Sie daran, dass der Benutzer konzeptionell einen Link zu einer Ressource speichert, nicht zu einer Version einer Darstellung einer Ressource.
Darrel Miller
11
@Gili Um die Anforderung zu erfüllen, dass eine REST-API selbstbeschreibend ist, muss der Inhaltstyp-Header die vollständige semantische Beschreibung der Nachricht enthalten. Mit anderen Worten, Ihr Medientyp ist Ihr Datenvertrag. Wenn Sie application / xml oder application / json bereitstellen, sagen Sie dem Client nichts darüber, was in diesem XML / Json enthalten ist. In dem Moment, in dem eine Clientanwendung einen Auszug / Kunden / Namen erreicht, erstellen Sie eine Kopplung, die auf Informationen basiert, die nicht in der Nachricht enthalten sind. Das Eliminieren der Out-of-Band-Kopplung ist entscheidend für das Erreichen von RESTfulness.
Darrel Miller
6
@Gili Der Client sollte keine anderen Vorkenntnisse über die URLs der API als die Root-URL haben. Sie sollten Darstellungsformate nicht an bestimmte URLs binden. Wenn Sie Medientypen auswählen, müssen Sie wirklich zwischen einem bestimmten Format wie application / vnd.mycompany.myformat + xml oder einem standardisierten Format wie XHtml, Atom, RDF usw. wählen.
Darrel Miller
4
Ist es sinnvoll, die API-Version als separates Header-Feld anzugeben? So: Akzeptieren: application / com.example.myapp + json; version = 1.0
Erik
21

Ah, ich setze meinen alten mürrischen Hut wieder auf.

Aus ReST-Sicht spielt es überhaupt keine Rolle. Keine Wurst.

Der Client erhält eine URI, der er folgen möchte, und behandelt sie als undurchsichtige Zeichenfolge. Geben Sie ein, was Sie wollen, der Client hat keine Kenntnis von einer Versionskennung.

Der Kunde weiß, dass er den Medientyp verarbeiten kann, und ich rate Ihnen, Darrels Rat zu befolgen. Ich persönlich bin auch der Meinung, dass die Notwendigkeit, das in einer erholsamen Architektur verwendete Format viermal zu ändern, große, massive Warnsignale mit sich bringen sollte, dass Sie etwas ernsthaft falsch machen, und die Notwendigkeit, Ihren Medientyp so zu gestalten, dass er widerstandsfähig ist, vollständig umgeht.

In beiden Fällen kann der Client ein Dokument nur in einem Format verarbeiten, das er verstehen kann, und den darin enthaltenen Links folgen. Es sollte über die Verknüpfungsbeziehungen (die Übergänge) Bescheid wissen. Was in der URI enthalten ist, ist also völlig irrelevant.

Ich persönlich würde für http: // localhost / 3f3405d5-5984-4683-bf26-aca186d21c04 stimmen

Eine vollkommen gültige Kennung, die verhindert, dass weitere Client-Entwickler oder Personen, die das System berühren, sich fragen, ob v4 am Anfang oder am Ende eines URI stehen soll (und ich schlage vor, dass Sie aus Server-Sicht keine 4 haben sollten Versionen, aber 4 Medientypen).

SerialSeb
quelle
Was ist, wenn sich die Darstellung erheblich ändern muss und nicht abwärtskompatibel ist?
Mike Pone
1
Durch das erweiterbare Entwerfen Ihres Medientyps, z. B. durch die Verwendung von Namespaces und einer erweiterbaren xsd oder vorhandenen XML-Formaten wie atom, sollte dies vermeidbar sein. Wenn Sie wirklich müssen, ist ein anderer Medientyp der richtige Weg.
SerialSeb
1
Ich mag diese vollständig gültige Antwort, aber ich denke, die vorgeschlagene URI dient eher dazu, den Punkt zu demonstrieren, als für ein reales Szenario, in dem Sie "hackbare" URIs wollen.
Dave Van den Eynde
10

Sie sollten die Version NICHT in die URL einfügen, Sie sollten die Version in den Accept Header der Anfrage einfügen - siehe meinen Beitrag zu diesem Thread:

Best Practices für die API-Versionierung?

Wenn Sie anfangen, Versionen in die URL einzufügen, erhalten Sie dumme URLs wie diese: http://company.com/api/v3.0/customer/123/v2.0/orders/4321/

Und es gibt eine Reihe anderer Probleme, die sich ebenfalls einschleichen - siehe meinen Blog: http://thereisnorightway.blogspot.com/2011/02/versioning-and-types-in-resthttp-api.html

jeremyh
quelle
11
Entschuldigung, aber ich glaube nicht, dass Sie am Ende solche dummen URLs haben. Sie binden Versionsnummern an eine bestimmte Ressource oder (schlimmer noch) an eine bestimmte Darstellung. Das wäre dumm, IMO. Sie versionieren vielmehr die API, sodass Sie nie mehr als eine Version in der URI haben würden.
narr4jesus
3

Es gibt 4 verschiedene Ansätze zur Versionierung der API:

  • Hinzufügen einer Version zum URI-Pfad:

    http://example.com/api/v1/foo
    
    http://example.com/api/v2/foo
    

    Wenn Sie Änderungen ändern, müssen Sie die Version wie folgt erhöhen: v1, v2, v3 ...

    Sie können einen Controller wie folgt in Ihren Code implementieren:

    @RestController
    public class FooVersioningController {
    
    @GetMapping("v1/foo")
    public FooV1 fooV1() {
        return new FooV1("firstname lastname");
    }
    
    @GetMapping("v2/foo")
    public FooV2 fooV2() {
        return new FooV2(new Name("firstname", "lastname"));
    }
    
  • Versionierung der Parameter anfordern:

    http://example.com/api/v2/foo/param?version=1
    http://example.com/api/v2/foo/param?version=2
    

    Der Versionsparameter kann optional oder erforderlich sein, je nachdem, wie die API verwendet werden soll.

    Die Implementierung kann folgendermaßen aussehen:

    @GetMapping(value = "/foo/param", params = "version=1")
    public FooV1 paramV1() {
        return new FooV1("firstname lastname");
    }
    
    @GetMapping(value = "/foo/param", params = "version=2")
    public FooV2 paramV2() {
        return new FooV2(new Name("firstname", "lastname"));
    }
    
  • Übergeben eines benutzerdefinierten Headers:

    http://localhost:8080/foo/produces
    

    Mit Header:

    headers[Accept=application/vnd.company.app-v1+json]
    

    oder:

    headers[Accept=application/vnd.company.app-v2+json]
    

    Der größte Vorteil dieses Schemas ist hauptsächlich die Semantik: Sie überladen den URI nicht mit irgendetwas, das mit der Versionierung zu tun hat.

    Mögliche Implementierung:

    @GetMapping(value = "/foo/produces", produces = "application/vnd.company.app-v1+json")
    public FooV1 producesV1() {
        return new FooV1("firstname lastname");
    }
    
    @GetMapping(value = "/foo/produces", produces = "application/vnd.company.app-v2+json")
    public FooV2 producesV2() {
        return new FooV2(new Name("firstname", "lastname"));
    }
    
  • Ändern von Hostnamen oder Verwenden von API-Gateways:

    Im Wesentlichen verschieben Sie die API von einem Hostnamen auf einen anderen. Sie können dies sogar einfach als Erstellen einer neuen API für dieselben Ressourcen bezeichnen.

    Sie können dies auch mit API-Gateways tun.

Javier C.
quelle
2

Wenn die REST-Services vor der Verwendung eine Authentifizierung erfordern, können Sie den API-Schlüssel / das API-Token problemlos einer API-Version zuordnen und das Routing intern durchführen. Um eine neue Version der API zu verwenden, ist möglicherweise ein neuer API-Schlüssel erforderlich, der mit dieser Version verknüpft ist.

Leider funktioniert diese Lösung nur für auth-basierte APIs. Versionen werden jedoch nicht in die URIs aufgenommen.

UberSteve
quelle
2

Ich wollte versionierte APIs erstellen und fand diesen Artikel sehr nützlich:

http://blog.steveklabnik.com/posts/2011-07-03-nobody-understands-rest-or-http

Es gibt einen kleinen Abschnitt zum Thema "Ich möchte, dass meine API versioniert wird". Ich fand es einfach und leicht zu verstehen. Der springende Punkt ist, das Feld Akzeptieren im Header zu verwenden, um Versionsinformationen zu übergeben.

Asma Zubair
quelle
1

Ich würde die Version als optionalen Wert am Ende des URI einfügen. Dies kann ein Suffix wie / V4 oder ein Abfrageparameter wie von Ihnen beschrieben sein. Sie können sogar / V4 zum Abfrageparameter umleiten, damit Sie beide Varianten unterstützen.

Paul Morgan
quelle
1

Wenn Sie URIs für die Versionierung verwenden, sollte sich die Versionsnummer im URI des API-Stamms befinden, damit jede Ressourcen-ID sie enthalten kann.

Technisch gesehen bricht eine REST-API nicht durch URL-Änderungen (das Ergebnis der einheitlichen Schnittstellenbeschränkung). Es wird nur unterbrochen, wenn sich die zugehörige Semantik (z. B. ein API-spezifisches RDF-Vokabular) nicht abwärtskompatibel ändert (selten). Derzeit verwenden viele Leute keine Links für die Navigation (HATEOAS-Einschränkung) und Vokabeln, um ihre REST-Antworten zu kommentieren (selbstbeschreibende Nachrichtenbeschränkung), weshalb ihre Clients brechen.

Benutzerdefinierte MIME-Typen und die Versionierung von MIME-Typen helfen nicht, da das Einfügen der zugehörigen Metadaten und der Struktur der Darstellung in eine kurze Zeichenfolge nicht funktioniert. Ofc. Die Metadaten und die Struktur ändern sich häufig, daher auch die Versionsnummer ...

Um Ihre Frage zu beantworten, können Sie Ihre Anfragen und Antworten am besten mit Vokabeln ( Hydra , verknüpfte Daten ) versehen und die Versionierung vergessen oder nur durch nicht abwärtskompatible Vokabularänderungen verwenden (z. B. wenn Sie ein Vokabular durch ein anderes ersetzen möchten).

inf3rno
quelle
0

Ich stimme dafür in MIME-Typ, aber nicht in URL. Aber der Grund ist nicht der gleiche wie bei anderen.

Ich denke, die URL sollte eindeutig sein (mit Ausnahme dieser Weiterleitungen), um die eindeutige Ressource zu finden. Also, wenn Sie /v2.0in URLs akzeptieren , warum ist es nicht /ver2.0oder /v2/oder /v2.0.0? Oder sogar -alphaund -beta? (dann wird es total zum Konzept des Semvers )

Daher ist die Version im MIME-Typ akzeptabler als die URL.

Yarco
quelle