Ich habe eine Objekthierarchie, die ich über eine RESTful-API verfügbar machen muss, und ich bin nicht sicher, wie meine URLs strukturiert sein sollen und was sie zurückgeben sollen. Ich konnte keine Best Practices finden.
Nehmen wir an, ich habe Hunde und Katzen, die von Tieren erben. Ich brauche CRUD-Operationen an Hunden und Katzen. Ich möchte auch in der Lage sein, Operationen an Tieren im Allgemeinen durchzuführen.
Meine erste Idee war, so etwas zu machen:
GET /animals # get all animals
POST /animals # create a dog or cat
GET /animals/123 # get animal 123
Die Sache ist, dass die / animal-Sammlung jetzt "inkonsistent" ist, da sie zurückkehren und Objekte aufnehmen kann, die nicht genau die gleiche Struktur haben (Hunde und Katzen). Wird es als "RESTful" angesehen, wenn eine Sammlung Objekte mit unterschiedlichen Attributen zurückgibt?
Eine andere Lösung wäre, eine URL für jeden konkreten Typ wie folgt zu erstellen:
GET /dogs # get all dogs
POST /dogs # create a dog
GET /dogs/123 # get dog 123
GET /cats # get all cats
POST /cats # create a cat
GET /cats/123 # get cat 123
Aber jetzt ist die Beziehung zwischen Hunden und Katzen verloren. Wenn alle Tiere abgerufen werden sollen, müssen sowohl die Hunde- als auch die Katzenressourcen abgefragt werden. Die Anzahl der URLs erhöht sich auch mit jedem neuen Tier-Subtyp.
Ein weiterer Vorschlag war, die zweite Lösung durch Hinzufügen zu ergänzen:
GET /animals # get common attributes of all animals
In diesem Fall würden die zurückgegebenen Tiere nur Attribute enthalten, die allen Tieren gemeinsam sind, wobei hundespezifische und katzenspezifische Attribute gelöscht würden. Dies ermöglicht das Abrufen aller Tiere, wenn auch mit weniger Details. Jedes zurückgegebene Objekt kann einen Link zur detaillierten, konkreten Version enthalten.
Irgendwelche Kommentare oder Vorschläge?
quelle
GET /animals - gets all dogs and cats
GET /animals/dogs - gets all dogs
GET /animals/cats - gets all cats
GET /animals
Akzeptierenapplication/vnd.vet-services.animal.dog+json
POST
Betrieb umgehen , da die meisten Frameworks nicht wissen, wie sie es ordnungsgemäß in ein Modell deserialisieren können, da json keine guten Tippinformationen enthält. Wie würden Sie zB mit Post-Fällen umgehen[{"type":"dog","name":"Fido","playsFetch":true},{"type":"cat","name":"Sparkles","likesToPurr":"sometimes"}
?dogs.likesToPurr
die nicht definiert ist. In einem lose strukturierten Framework wie Flask können Sietype
zuerst das Attribut überprüfen und dann das Objekt je nach Typ zusammenstellen.Ich würde für / Tiere gehen und eine Liste von Hunden und Fischen zurückgeben und was auch immer:
Es sollte einfach sein, ein ähnliches JSON-Beispiel zu implementieren.
Clients können sich immer darauf verlassen, dass das Element "name" vorhanden ist (ein gemeinsames Attribut). Abhängig vom Attribut "Typ" gibt es jedoch andere Elemente als Teil der Tierdarstellung.
Es gibt nichts von Natur aus RESTful oder unRESTful bei der Rückgabe einer solchen Liste - REST schreibt kein bestimmtes Format für die Darstellung von Daten vor. Es sagt nur, dass Daten müssen einig Darstellung und das Format für die Darstellung durch den Medientyp (die in HTTP ist der Content-Type - Header) identifiziert.
Denken Sie über Ihre Anwendungsfälle nach - müssen Sie eine Liste gemischter Tiere anzeigen? Dann geben Sie eine Liste mit gemischten Tierdaten zurück. Benötigen Sie nur eine Liste von Hunden? Nun, machen Sie eine solche Liste.
Ob Sie / Tiere? Typ = Hund oder / Hunde tun, ist in Bezug auf REST irrelevant, das keine URL-Formate vorschreibt - dies bleibt als Implementierungsdetail außerhalb des Bereichs von REST. REST gibt nur an, dass Ressourcen Bezeichner haben sollten - egal welches Format.
Sie sollten einige Hyper-Media-Links hinzufügen, um einer RESTful-API näher zu kommen. Zum Beispiel durch Hinzufügen von Verweisen auf die Tierdetails:
Durch Hinzufügen von Hyper-Media-Links reduzieren Sie die Client / Server-Kopplung. In diesem Fall entlasten Sie den Client von der URL-Erstellung und lassen den Server entscheiden, wie URLs erstellt werden sollen (von denen er per Definition die einzige Autorität ist).
quelle
Diese Frage kann mit der Unterstützung einer kürzlich in der neuesten Version von OpenAPI eingeführten Erweiterung besser beantwortet werden.
Es war möglich, Schemas mit Schlüsselwörtern wie oneOf, allOf, anyOf zu kombinieren und eine seit JSON-Schema v1.0 validierte Nachrichtennutzlast zu erhalten.
https://spacetelescope.github.io/understanding-json-schema/reference/combining.html
In OpenAPI (ehemals Swagger) wurde die Schemakomposition jedoch durch die Schlüsselwörter Diskriminator (v2.0 +) und oneOf (v3.0 +) verbessert, um den Polymorphismus wirklich zu unterstützen.
https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#schemaComposition
Ihre Vererbung kann mithilfe einer Kombination aus oneOf (zur Auswahl eines der Subtypen) und allOf (zur Kombination des Typs und eines seiner Subtypen) modelliert werden. Unten finden Sie eine Beispieldefinition für die POST-Methode.
quelle
Beachten Sie jedoch, dass URI niemals die Beziehungen zwischen Objekten widerspiegelt.
quelle
Ich weiß, dass dies eine alte Frage ist, aber ich bin daran interessiert, weitere Probleme bei einer RESTful-Vererbungsmodellierung zu untersuchen
Ich kann immer sagen, dass ein Hund ein Tier und auch eine Henne ist, aber eine Henne macht Eier, während ein Hund ein Säugetier ist, also kann es nicht. Eine API wie
GET Tiere /: animalID / Eier
ist nicht konsistent, da dies darauf hinweist, dass alle Subtypen von Tieren Eier haben können (als Folge der Liskov-Substitution). Es würde einen Fallback geben, wenn alle Säugetiere mit '0' auf diese Anfrage antworten, aber was ist, wenn ich auch eine POST-Methode aktiviere? Sollte ich befürchten, dass morgen Hundeeier in meinen Crepes sein würden?
Die einzige Möglichkeit, mit diesen Szenarien umzugehen, besteht darin, eine "Superressource" bereitzustellen, die alle Teilressourcen zusammenfasst, die von allen möglichen "abgeleiteten Ressourcen" gemeinsam genutzt werden, und dann eine Spezialisierung für jede abgeleitete Ressource, die sie benötigt, genau wie beim Downcasting eines Objekts in oop
GET / Tiere /: animalID / Söhne GET / Hühner /: animalID / Eier POST / Hühner /: animalID / Eier
Der Nachteil hierbei ist, dass jemand eine Hunde-ID übergeben könnte, um auf eine Instanz der Hühnersammlung zu verweisen, aber der Hund ist keine Henne, daher wäre es nicht falsch, wenn die Antwort 404 oder 400 mit einer Grundmeldung wäre
Liege ich falsch?
quelle
Ja, du liegst falsch. Auch Beziehungen können gemäß den OpenAPI-Spezifikationen modelliert werden, z. B. auf diese polymorphe Weise.
...
quelle
GET chicken/eggs
sollte auch mit den gängigen OpenAPI-Codegeneratoren für die Controller funktionieren, aber ich habe dies noch nicht überprüft. Vielleicht kann es jemand versuchen?