Gibt es Probleme bei der Implementierung benutzerdefinierter HTTP-Methoden?

34

Wir haben eine URL im folgenden Format

/ instance / {instanceType} / {instanceId}

Sie können es mit den Standard-HTTP-Methoden aufrufen: POST, GET, DELETE, PUT. Es gibt jedoch noch einige weitere Aktionen, die wir durchführen, z. B. "Als Entwurf speichern" oder "Kuratieren".

Wir dachten, wir könnten einfach benutzerdefinierte HTTP-Methoden verwenden, wie ENTWURF, GÜLTIG, KURAT

Ich denke, das ist akzeptabel, da die Standards sagen

"Der Satz allgemeiner Methoden für HTTP / 1.1 wird nachstehend definiert. Obwohl dieser Satz erweitert werden kann, kann nicht davon ausgegangen werden, dass zusätzliche Methoden dieselbe Semantik für separat erweiterte Clients und Server verwenden."

Und Tools wie WebDav erstellen einige ihrer eigenen Erweiterungen.

Gibt es Probleme mit benutzerdefinierten Methoden? Ich denke an Proxy-Server und Firewalls, aber auch andere Bereiche sind willkommen. Sollte ich auf der sicheren Seite bleiben und nur einen URL-Parameter wie action = validate | curate | draft haben?

Juan Mendes
quelle
6
Ich zitiere noch einmal aus RFC 1925 : "Im Protokolldesign wurde Perfektion nicht erreicht, wenn nichts mehr hinzuzufügen ist, sondern wenn nichts mehr wegzunehmen ist." - Wenn es funktioniert, gibt es keinen Grund, http hinzuzufügen.
4
Nichts falsches, solange Sie feststellen, dass Sie jetzt ein benutzerdefiniertes Protokoll und nicht HTTP verwenden.
user16764
10
@ user16764 "Der Satz allgemeiner Methoden für HTTP / 1.1 ist unten definiert. Obwohl dieser Satz erweitert werden kann, kann nicht davon ausgegangen werden, dass zusätzliche Methoden dieselbe Semantik für separat erweiterte Clients und Server verwenden." w3.org/Protocols/rfc2616/rfc2616-sec9.html Deshalb ist es erlaubt, und es ist immer noch HTTP
Juan Mendes
Imho gibt es nichts hinzuzufügen / zu entfernen, da die Methodendefinitionen besagen, dass die Verwendung von benutzerdefinierten Methoden im HTTP / 1.1-Bereich bereits akzeptabel ist, jedoch nicht die gleiche Semantik erwartet werden kann etwas beschwichtigt sein
Prof83

Antworten:

42

Eine der grundlegenden Einschränkungen von HTTP und der zentralen Entwurfsfunktion von REST ist eine einheitliche Schnittstelle, die unter anderem von einer kleinen, festen Gruppe von Methoden bereitgestellt wird, die universell für alle Ressourcen gelten. Die einheitliche Schnittstellenbeschränkung weist eine Reihe von Vor- und Nachteilen auf. Ich zitiere hier großzügig aus Fielding .

Eine einheitliche Schnittstelle:

  • ist einfacher.
  • entkoppelt Implementierungen von den von ihnen bereitgestellten Diensten.
  • ermöglicht eine mehrschichtige Architektur, einschließlich HTTP-Load-Balancer (Nginx) und Caches (Lack).

Zum anderen eine einheitliche Oberfläche:

  • Dies beeinträchtigt die Effizienz, da Informationen in standardisierter Form übertragen werden und nicht in einer Form, die auf die Anforderungen einer Anwendung zugeschnitten ist.

Die Kompromisse sind "für den allgemeinen Fall des Webs ausgelegt" und haben den Aufbau eines großen Ökosystems ermöglicht, das Lösungen für viele der allgemeinen Probleme in Webarchitekturen bietet. Das Festhalten an einer einheitlichen Schnittstelle ermöglicht es Ihrem System, von diesem Ökosystem zu profitieren, während ein Bruch es so schwierig macht. Möglicherweise möchten Sie einen Load Balancer wie nginx verwenden, aber jetzt können Sie nur einen Load Balancer verwenden, der DRAFT und CURATE versteht. Möglicherweise möchten Sie eine HTTP-Cache-Ebene wie Varnish verwenden, aber jetzt können Sie nur eine HTTP-Cache-Ebene verwenden, die DRAFT und CURATE versteht. Möglicherweise möchten Sie jemanden um Hilfe bei der Behebung eines Serverfehlers bitten, aber niemand kennt die Semantik für eine CURATE-Anforderung. Es kann schwierig sein, Ihre bevorzugten Client- oder Server-Bibliotheken zu ändern, um die neuen Methoden zu verstehen und korrekt zu implementieren. Und so weiter.

Die korrekte Darstellung * erfolgt als Statusumwandlung für die Ressource (oder verwandte Ressourcen). Sie ENTWURFEN keinen Beitrag, Sie transformieren seinen draftStatus in trueoder Sie erstellen eine draftRessource, die die Änderungen und Links zu früheren Entwurfsversionen enthält. Sie KURIEREN einen Beitrag nicht, Sie wandeln seinen curatedStatus in trueeine curationRessource um oder erstellen eine Ressource, die den Beitrag mit dem Benutzer verknüpft, der ihn kuratiert hat.

* Richtig, da es den REST-Architekturprinzipien am nächsten kommt.

Rein Henrichs
quelle
Danke für die Kommentare zum Load Balancing, ich werde mir das auf jeden Fall ansehen. Kennen Sie eine Ressource, die angibt, ob benutzerdefinierte Methoden zulässig sind oder nicht?
Juan Mendes
2
Ich sehe keinen Vorteil für benutzerdefinierte Methoden, es sei denn, sie sind Teil einer allgemein unterstützten Erweiterung wie WEBDAV (und selbst dann nicht so sehr), so dass ich mich noch nie damit befasst habe. Ich würde Ihnen nur empfehlen, diese Änderungen als Statusumwandlungen zu behandeln. Das Web funktioniert gut mit den Methoden, die wir bereits haben. Es gibt wirklich keinen guten Grund, mehr hinzuzufügen, es sei denn, sie sind als Teil der einheitlichen Schnittstelle (wie PATCH) sinnvoll.
Rein Henrichs
5
Ich sehe den Vorteil darin, dass Sie selbst festlegen, wie Ihr HTTP-Dienst funktionieren soll. Es kann jedoch nicht davon ausgegangen werden, dass "zusätzliche Methoden dieselbe Semantik haben". Allerdings ist dies immer noch Teil des Geltungsbereichs von HTTP / 1.1. Daher sollten Firewalls, Proxys, Load Balancer und dergleichen diese Möglichkeit in Betracht ziehen, wenn sie nicht zutreffen. Implementieren sie dann HTTP / 1.1 nicht richtig?
Prof83
Sie haben wahrscheinlich Recht mit dem restlichen POV, aber ich kann nicht verstehen, warum benutzerdefinierte Verben ein Problem sein sollten. Alle Tools sollten sie wie POST behandeln, dh "die Ressource ändert sich wahrscheinlich und das ist alles, was wir wissen".
Maaartinus
7

Ich würde es vorziehen, diese als Subressourcen zu gestalten, für die Sie eine POST-Anfrage durchführen.

Angenommen, Sie haben eine Ressource unter /instance/type/1, dann würde die Darstellung dieser Ressource einige Links zu "Aktionen" enthalten, die für die Ressource ausgeführt werden können, z. B. /instance/type/1/draftund /instance/type/1/curate. In JSON könnte dies so einfach sein:

{
    "some property":"the usual value",
    "state": "we can still inform the client about the current state",
    "draft": "http://server/instance/type/1/draft",
    "curate": "http://server/instance/type/1/curate"
}

Auf diese Weise kann der Client während der POST-Anforderung an den vom curateMitglied bereitgestellten Link sehr genau angeben, was zu tun ist . Die dort veröffentlichte Ressource könnte Argumente enthalten, die das Ereignis detailliert beschreiben, das möglicherweise einen Zustandsübergang verursachen würde.

Die "naive" Herangehensweise, zwischen den möglichen Zuständen einer Ressource zu wechseln, hat den Nachteil, nicht zu erfassen, welche Ereignisse zu diesen Übergängen geführt haben.

Zustandsübergänge treten normalerweise als Reaktion auf bestimmte Ereignisse auf, und ich möchte diese Ereignisse lieber erfassen, als den Client entscheiden zu lassen, dass sich etwas in einem bestimmten "Zustand" befindet. Dies erschwert auch die Validierung erheblich. Außerdem könnten Sie keine „Argumente“ erfassen, wenn Sie nicht auch die im Staat selbst beschreiben. Und dann wird es icky, wenn ein Code diejenigen ohne echten Zustandsübergang ändert und die erforderliche Validierung und das Ganze schnell zu einem Chaos wird.

Dave Van den Eynde
quelle
Gute Antwort. Argumente für Zustandsübergänge bereitzustellen und den Server diese kapseln und verwalten zu lassen, ist bei weitem der beste Ansatz.
Thomas W
Die Firma, bei der ich mich gerade befinde (VMware), tut dies. Ein GET on /vms/some-idgibt Links zu Aktionen wie zurück POST /vms/some-id/restartund wir bestimmen damit, ob Aktionen aktiviert oder deaktiviert werden sollen. Ich habe eine Hassliebe zu HATEOAS :)
Juan Mendes
Es wäre viel sinnvoller, wenn die auszuführende Aktion das Verb der Anforderung im Vergleich zu einem zufälligen Abfrageparameter, einem Ressourcenpfadsegment oder einer Body-Eigenschaft wäre.
Matthew Whited
Sie können nicht zu einem Verb verlinken.
Dave Van den Eynde
6

Ich denke, benutzerdefinierte HTTP-Methode ist der beste Weg, um Entity-Aktionen zu implementieren. Das Hinzufügen der Aktion zum Entitätshauptteil (POST) scheint nicht richtig zu sein, da es nicht zu Ihrer Entität gehört (obwohl das Ergebnis möglicherweise darin gespeichert wird). Mithilfe der benutzerdefinierten HTTP-Methoden können Proxys auch ihre Aktionen bestimmen, ohne dass der Entitätskörper analysiert werden muss.

Es ist wie bei CRUD, Sie würden diese immer implementieren wollen, aber Sie haben auch Ihre eigenen spezifischen Aktionen (je nach Aktivität). Ich verstehe wirklich nicht, was das Problem wäre, diese zu erweitern.

Auch @Rein Henrichs "Sie ENTWURFEN keinen Beitrag, Sie wandeln seinen Entwurfsstatus in" true "um oder Sie erstellen einen Entwurf für eine Ressource" scheint mir falsch zu sein. Eine draftsEigenschaft wird zum dauerhaften Speichern des Zustands und nicht zum Durchführen der Umwandlung verwendet. Aktionen führen nicht unbedingt zu einem 'Zustand' oder werden in einer Eigenschaft gespeichert. Das Erstellen einer separaten Entität für jeden Zustand / jede Transformation scheint noch unscharfer zu sein. Versuchen Sie, den gleichen Verweis (URI) auf die Einheit beizubehalten.

Robert de W
quelle
1
Dies ist ein fairer Punkt, auch wenn ich weitestgehend anderer Meinung bin. Ich kann die Gründe dafür sehen und bin nicht mit der Gegenstimme einverstanden (insbesondere ohne Kommentar des Wählers). Nehmen wir als Beispiel die Behandlung von PHP-Ausnahmen. "Best Practice" scheint darauf abzustellen, bestimmte Ausnahmetypen zu verwenden, um die Art der Ausnahme vorzuschlagen, und ignoriert sogar die eigentliche Meldung wie RuntimeException vs BadMethodCallException. Warum wird die Verwendung von DRAFT so häufig abgelehnt, wenn die Verwendung benutzerdefinierter Methoden bereits als Teil des Geltungsbereichs von HTTP / 1.1 betrachtet wird? Und Load Balancer und Proxys sollten diese Möglichkeit auch wirklich akzeptieren
Prof83