Ich plane, eine RESTfull-API zu erstellen, aber es gibt einige architektonische Fragen, die Probleme in meinem Kopf verursachen. Das Hinzufügen von Back-End-Geschäftslogik zu Clients ist eine Option, die ich vermeiden möchte, da das Aktualisieren mehrerer Clientplattformen in Echtzeit schwierig ist, wenn sich die Geschäftslogik schnell ändern kann.
Nehmen wir an, wir haben Artikel als Ressource (API / Artikel). Wie sollten wir Aktionen wie Veröffentlichen, Aufheben der Veröffentlichung, Aktivieren oder Deaktivieren usw. implementieren, aber versuchen, dies so einfach wie möglich zu halten?
1) Sollten wir api / article / {id} / {action} verwenden, da dort eine Menge Backend-Logik vorkommen kann, z. B. das Verschieben an entfernte Standorte oder das Ändern mehrerer Eigenschaften. Das Schwierigste hierbei ist wahrscheinlich, dass wir alle Artikeldaten zur Aktualisierung an die API zurücksenden müssen und die Mehrbenutzerarbeit nicht implementiert werden konnte. Zum Beispiel könnte der Redakteur 5 Sekunden ältere Daten senden und die Korrektur überschreiben, die ein anderer Journalist gerade vor 2 Sekunden vorgenommen hat, und ich kann dies den Kunden auf keinen Fall erklären, da diejenigen, die einen Artikel veröffentlichen, in keiner Weise mit der Aktualisierung des Inhalts verbunden sind.
2) Das Erstellen einer neuen Ressource kann auch eine Option sein, api / article- {action} / id, aber dann wäre die zurückgegebene Ressource nicht article- {action}, sondern article, bei dem ich nicht sicher bin, ob dies richtig ist. Auch im serverseitigen Code behandelt die Artikelklasse die aktuelle Arbeit an beiden Ressourcen, und ich bin mir nicht sicher, ob dies gegen das REST-Denken verstößt
Anregungen sind willkommen ..
api/article?action=publish
? Abfrageparameter sind für solche Fälle vorgesehen, in denen der Status der Ressource von dem von Ihnen genannten 'Algorithmus' (oder der Aktion) abhängt. ZBapi/articles?sort=asc
ist gültigAntworten:
Ich finde die hier beschriebenen Praktiken hilfreich:
quelle
/article/123/deactivations
, um eine neue Deaktivierungsanforderung für den Artikel 123 zu erstellen, deaktiviert der Server möglicherweise nicht nur die angeforderte Ressource, sondern speichert meine Deaktivierungsanforderung tatsächlich, damit ich ihren Status später abrufen kann.PUT /gists/:id/star
nichtPOST /gists/:id/star
?Vorgänge, die zu wesentlichen Zustands- und Verhaltensänderungen auf der Serverseite führen, wie die von Ihnen beschriebene "Publizieren" -Aktion, sind in REST nur schwer explizit zu modellieren. Eine Lösung, die ich oft sehe, besteht darin, ein derart komplexes Verhalten implizit durch Daten zu steuern.
Erwägen Sie, Waren über eine REST-API zu bestellen, die von einem Online-Händler bereitgestellt wird. Bestellung ist ein komplexer Vorgang. Mehrere Produkte werden verpackt und versendet, Ihr Konto wird belastet und Sie erhalten eine Quittung. Sie können Ihre Bestellung für eine begrenzte Zeit stornieren und es gibt natürlich eine vollständige Geld-zurück-Garantie, mit der Sie Produkte gegen eine Rückerstattung zurücksenden können.
Anstelle eines komplexen Einkaufsvorgangs können Sie mit einer solchen API möglicherweise eine neue Ressource, eine Bestellung, erstellen. Zu Beginn können Sie beliebige Änderungen daran vornehmen: Hinzufügen oder Entfernen von Produkten, Ändern der Lieferadresse, Auswählen einer anderen Zahlungsoption oder Stornieren Ihrer Bestellung insgesamt. Sie können all dies tun, weil Sie noch nichts gekauft haben. Sie manipulieren nur einige Daten auf dem Server.
Sobald Ihre Bestellung abgeschlossen ist und Ihre Nachfrist abgelaufen ist, sperrt der Server Ihre Bestellung, um weitere Änderungen zu verhindern. Erst zu diesem Zeitpunkt beginnt die komplexe Abfolge von Vorgängen, die Sie jedoch nicht direkt steuern können, sondern nur indirekt über die Daten, die Sie zuvor in die Bestellung eingegeben haben.
Basierend auf Ihrer Beschreibung könnte "Publizieren" auf diese Weise implementiert werden. Anstatt eine Operation verfügbar zu machen, platzieren Sie eine Kopie des geprüften Entwurfs und möchten ihn unter / publish als neue Ressource veröffentlichen. Dadurch wird sichergestellt, dass spätere Aktualisierungen des Entwurfs nicht veröffentlicht werden, selbst wenn der Veröffentlichungsvorgang selbst Stunden später abgeschlossen wird.
quelle
Dies ist eine Herausforderung, egal was Sie tun. Es ist ein sehr ähnliches Problem wie bei der verteilten Quellcodeverwaltung (Quecksilber, Git usw.), und die in HTTP / ReST geschriebene Lösung sieht ein bisschen ähnlich aus.
Angenommen, Sie haben zwei Benutzer, Alice und Bob, an denen beide arbeiten
/articles/lunch
. (Zur Verdeutlichung ist die Antwort fett gedruckt.)Zunächst erstellt Alice den Artikel.
Der Server hat keine Ressource erstellt, da an die Anforderung keine "Version" angehängt war (unter der Annahme eines Bezeichners von
/articles/{id}/{version}
. Um die Erstellung durchzuführen, wurde Alice an die URL des Artikels / der Version weitergeleitet, den / die sie erstellen wird. Benutzer von Alice Der Agent wendet die Anforderung dann erneut an der neuen Adresse an.Und jetzt wurde der Artikel erstellt. Als nächstes schaut Bob sich den Artikel an:
Bob schaut dorthin:
Er beschließt, sein eigenes Wechselgeld hinzuzufügen.
Wie bei Alice wird Bob an den Ort weitergeleitet, an dem er eine neue Version erstellen wird.
Schließlich entscheidet Alice, dass sie ihren eigenen Artikel hinzufügen möchte:
Anstatt wie gewohnt umgeleitet zu werden, wird ein anderer Statuscode an den Client zurückgegeben,
409
der Alice mitteilt, dass die Version, von der sie abzweigen wollte, bereits verzweigt wurde. Die neuen Ressourcen wurden trotzdem erstellt (wie in derLocation
Kopfzeile gezeigt), und die Unterschiede zwischen den beiden wurden in den Antworttext aufgenommen. Alice weiß jetzt, dass die Anfrage, die sie gerade gestellt hat, irgendwie zusammengeführt werden muss.All diese Umleitungen hängen mit der Semantik von zusammen
PUT
, die es erforderlich macht, dass neue Ressourcen genau dort erstellt werden, wo die Anforderungszeile dies verlangt. Dies könntePOST
stattdessen auch einen Anforderungszyklus mit speichern , aber dann müsste die Versionsnummer in der Anforderung durch eine andere Magie codiert werden, die mir zum Zwecke der Veranschaulichung weniger offensichtlich erschien, in einer echten API jedoch wahrscheinlich immer noch bevorzugt wird um Anforderungs- / Antwortzyklen zu minimieren.quelle
Hier ist ein weiteres Beispiel, das sich nicht mit Dokumenteninhalten befasst, sondern eher mit vorübergehenden Zuständen. (Ich finde die Versionierung - da im Allgemeinen jede Version eine neue Ressource sein kann - eine Art einfaches Problem.)
Angenommen, ich möchte einen Dienst, der auf einem Computer ausgeführt wird, über einen REST verfügbar machen, damit er gestoppt, gestartet, neu gestartet und so weiter werden kann.
Was ist hier der RESTVOLLSTE Ansatz? POST / service? Command = restart zum Beispiel? Oder POST / service / state mit einem Körper von 'running'?
Es wäre schön, hier Best Practices zu kodifizieren und zu prüfen, ob REST der richtige Ansatz für diese Art von Situation ist.
Zweitens nehmen wir an, dass ich eine Aktion von einem Dienst aus ausführen möchte, die sich nicht auf den eigenen Status auswirkt, sondern einen Nebeneffekt auslöst. Zum Beispiel ein Mail-Dienst, der einen zum Zeitpunkt des Anrufs erstellten Bericht an eine Reihe von E-Mail-Adressen sendet.
GET / report ist möglicherweise eine Möglichkeit, eine Kopie des Berichts selbst zu erhalten. aber was ist, wenn wir weitere Aktionen wie E-Mail, wie ich oben sage, auf den Server pushen möchten. Oder in eine Datenbank schreiben.
In diesen Fällen geht es um Ressourcenteilung, und ich sehe Möglichkeiten, sie auf REST-orientierte Weise zu behandeln, aber ehrlich gesagt fühlt es sich ein bisschen nach Hack an, dies zu tun. Vielleicht ist die Schlüsselfrage, ob eine REST-API im Allgemeinen Nebenwirkungen unterstützen sollte.
quelle
REST ist datenorientiert und als solche funktionieren Ressourcen am besten als "Dinge", nicht als Aktionen. Die implizite Semantik von http-Methoden; GET, PUT, DELETE usw. dienen dazu, die Orientierung zu verstärken. POST ist natürlich die Ausnahme.
Eine Ressource kann eine Mischung aus Daten sein, z. Artikelinhalt; und Metadaten dh. veröffentlicht, gesperrt, Revision. Es gibt viele andere Möglichkeiten, die Daten aufzuteilen, aber Sie müssen zuerst durchgehen, wie der Datenfluss aussehen wird, um den optimalen zu bestimmen (falls es einen gibt). Zum Beispiel kann es sein, dass Revisionen ihre eigene Ressource unter dem Artikel sein sollten, wie TokenMacGuy vorschlägt.
In Bezug auf die Implementierung würde ich wahrscheinlich etwas tun, was TockenMacGuy vorschlägt. Ich würde auch ein Metadatenfeld zum Artikel hinzufügen, nicht zur Revision, wie 'gesperrt' und 'veröffentlicht'.
quelle
Betrachten Sie es nicht als direkte Manipulation des Status des Artikels. Stattdessen geben Sie einen Änderungsauftrag ein , um die Erstellung des Artikels anzufordern.
Sie können das Einfügen eines Änderungsauftrags als das Erstellen einer neuen Änderungsauftragsressource (POST) modellieren. Es gibt viele Vorteile. Sie können beispielsweise ein zukünftiges Datum und eine zukünftige Uhrzeit angeben, zu der der Artikel als Teil des Änderungsauftrags veröffentlicht werden soll, und den Server sich Gedanken darüber machen lassen, wie dies implementiert wird.
Wenn das Veröffentlichen kein sofortiger Vorgang ist, müssen Sie nicht warten, bis der Vorgang abgeschlossen ist, bevor Sie zum Client zurückkehren. Sie bestätigen lediglich, dass der Änderungsauftrag erstellt wurde, und geben die Änderungsauftrags-ID zurück. Sie können dann die dieser Änderungsreihenfolge entsprechende URL verwenden, um den Status der Änderungsreihenfolge mitzuteilen.
Eine wichtige Erkenntnis für mich war, dass das Erkennen dieser Metapher für die Änderungsreihenfolge nur ein weiterer Weg ist, um die objektorientierte Programmierung zu beschreiben. Anstelle von Ressourcen nennen wir dann Objekte. Anstelle von Änderungsaufträgen nennen wir sie Nachrichten. Eine Möglichkeit, eine Nachricht von A nach B in OO zu senden, besteht darin, dass A eine Methode auf B aufruft Senden Sie es an B. REST formalisiert diesen Prozess einfach.
quelle
Wenn ich Sie richtig verstehe, ist das, was Sie haben, meines Erachtens eher ein Problem mit der Bestimmung von Geschäftsregeln als ein technisches Problem.
Die Tatsache, dass ein Artikel überschrieben werden kann, kann durch die Einführung von Berechtigungsstufen behoben werden, bei denen ältere Benutzer Versionen von jüngeren Benutzern überschreiben können. Außerdem können Versionen sowie eine Spalte zum Erfassen des Status des Artikels (z. B. "in Entwicklung", "endgültig") eingeführt werden. usw.), könnten Sie dies überwinden. Sie können dem Benutzer auch die Möglichkeit geben, eine bestimmte Version entweder nach einer Kombination aus dem Zeitpunkt der Übermittlung und der Versionsnummer auszuwählen.
In allen oben genannten Fällen muss Ihr Dienst die von Ihnen festgelegten Geschäftsregeln implementieren. Sie können den Service also mit den folgenden Parametern aufrufen: Benutzer-ID, Artikel, Version, Aktion (wobei die Version optional ist, hängt dies wiederum von Ihren Geschäftsregeln ab).
quelle