Opérations non CRUD dans un service RESTful

106

Quelle est la manière «RESTful» d'ajouter des opérations non CRUD à un service RESTful? Disons que j'ai un service qui permet à CRUD d'accéder à des enregistrements comme celui-ci:

GET /api/car/123           <- Returns information for the Car object with ID 123
POST /api/car              <- Creates a new car (with properties in the request)
PUT /api/car/123           <- Updates car 123 (with properties in the request)
DELETE /api/car/123        <- Deletes car 123    
POST /api/car/123/wheel/   <- Creates a wheel and associates it to car 123

Si je veux changer la couleur de la voiture, je voudrais simplement POST /api/car/123et inclure une variable POST pour la nouvelle couleur.

Mais disons que je veux acheter une voiture, et cette opération est plus compliquée que la simple mise à jour de la propriété «voiture possédée» d'un enregistrement «utilisateur». Est-il RESTful de simplement faire quelque chose comme POST /api/car/123/purchase, où «achat» est essentiellement un nom de méthode? Ou devrais-je utiliser un verbe HTTP personnalisé, comme PURCHASEau lieu de POST?

Ou les opérations non CRUD sont-elles complètement hors du champ d'application de REST?

MikeWyatt
la source
5
Si vous changez la couleur d'une voiture, il serait préférable d'utiliser PATCH /api/car/123et d'envoyer un paramètre de couleur OU d'utiliser PUT /api/car/123et d'envoyer tout l'objet voiture. POST inférerait que vous créez une nouvelle voiture et ne devrait probablement jamais inclure un identifiant à la fin de l'URL
RonnyKnoxville

Réponses:

65

Pensez à l' achat en tant qu'entité commerciale ou ressource dans le dictionnaire RESTful. Cela étant dit, faire un achat crée en fait une nouvelle ressource. Alors:

POST /api/purchase

passera une nouvelle commande. Les détails (utilisateur, voiture, etc.) doivent être référencés par identifiant (ou URI) à l'intérieur du contenu envoyé à cette adresse.

Peu importe que commander une voiture ne soit pas qu'un simple INSÉRER dans la base de données. En fait, REST ne consiste pas à exposer vos tables de base de données en tant qu'opérations CRUD. Du point de vue logique, vous créez une commande (achat), mais le côté serveur est libre d'effectuer autant d'étapes de traitement qu'il le souhaite.

Vous pouvez même abuser encore plus du protocole HTTP. Utilisez l'en- Locationtête pour renvoyer un lien vers la commande nouvellement créée, choisissez soigneusement les codes de réponse HTTP pour informer les utilisateurs des problèmes (côté serveur ou côté client), etc.

Tomasz Nurkiewicz
la source
3
REST consiste à manipuler l'état des ressources et chaque opération commerciale doit être mappée aux opérations CRUD d'état. Si vous avez besoin d' une sémantique pour les opérations métier, vous devrez suivre la voie SOAP (SOAP est en fait le passage de messages, mais est généralement organisé en opérations de demande-réponse).
Tomasz Nurkiewicz
23
La conception «achat en tant que ressource» semble soignée. Et si la ressource est une "bière" .. et je veux que le serveur la boive .. (c'était pour moi, je l'aurais sûrement;)) .. devrions-nous considérer l'action "boire" comme une ressource ?! .. ou est-ce que «boire une bière» est une opération commerciale difficile ?! Plus sérieusement, la conception RESTful consiste-t-elle à considérer les actions comme des ressources?! ..
Myobis
2
Comment exposeriez-vous "approuver la commande" via un service REST? Je pense que @TomaszNurkiewicz a raison en ce sens que tout ce qui ne peut pas être fait proprement d'une manière CRUD aura besoin de la sémantique opérationnelle fournie par SOAP. Sauf si «l'approbation des bons de commande» est un modèle / une entité en soi. Par exemple, l'approbation POST / PO (avec les détails du bon de commande dans la demande).
mydoghasworms
2
Du point de vue d'un client REST, «approuver la commande d'achat» ne devrait être qu'une autre mise à jour de la commande. Par exemple, changez «Approuvé» en «vrai» et envoyez la mise à jour au serveur. Le serveur devra probablement faire un tas de vérifications et il devra probablement mettre à jour / créer un tas d'autres ressources. Mais c'est le problème des serveurs et ne devrait pas être visible pour le client.
AVee
2
@antinome: "Supposons que le client en sache une partie", si tel est le cas, vous ne faites pas de REST (il se peut que ce soit toujours un logiciel valide et sensé!). REST a été conçu pour pouvoir créer des clients qui ne connaissent pas ce genre de choses, pour créer des clients qui fonctionnent toujours si le comportement du serveur change. Ce que vous essayez de faire est le RPC classique, vous devez soit revoir votre approche pour qu'elle s'adapte à REST, soit accepter que vous faites du RPC et utiliser un protocole destiné au RPC comme SOAP. REST essaie très fort de ne pas être RPC, donc il ne sera jamais un bon choix lorsque vous voulez / avez besoin de RPC.
AVee
15

La manière RESTful si je comprends bien, c'est que vous n'avez pas besoin de nouveaux verbes HTTP, il y a un nom quelque part qui signifiera ce que vous devez faire.

Acheter une voiture? Eh bien n'est-ce pas

POST /api/order
djna
la source
2
PUT n'est-il pas utilisé pour mettre à jour les ressources car il est idempotent? Cela signifie que vous pouvez l'appeler autant de fois que vous le souhaitez, mais seul le premier / dernier appel est important. POST d'autre part est utilisé pour créer des ressources et l'appeler deux fois devrait en fait en créer deux.
Tomasz Nurkiewicz
1
@Tomas, oui, faute de frappe. Le principe est important cependant, nous avons affaire à une nouvelle chose, un ordre, pas besoin d'un nouveau verbe.
djna le
5

Ce que vous faites vraiment, c'est créer une commande. Ajoutez donc une autre ressource pour la commande et la publication et mettez-la là pendant le processus de commande.

Pensez en termes de ressources plutôt que d'appels de méthodes.

Pour finaliser la commande, vous devez probablement POST / api / order // complet ou quelque chose de similaire.

Andrew Kothmann
la source
3

Je pense que les API REST aident de bien plus de manières que la simple fourniture de sémantique. Vous ne pouvez donc pas choisir le style RPC uniquement à cause de certains appels qui semblent avoir plus de sens dans le style d'opération RPC. Un exemple est l'API google maps pour trouver des directions entre deux endroits. Cela ressemble à ceci: http://maps.googleapis.com/maps/api/directions/json?origin=Jakkur&destination=Hebbal

Ils auraient pu l'appeler "findDirections" (verbe) et le traiter comme une opération. Au contraire, ils ont fait de la "direction" (nom) une ressource et ont traité la recherche de directions comme une requête sur la ressource de directions (bien qu'en interne, il ne puisse y avoir de véritable ressource appelée direction et elle pourrait être implémentée par la logique métier pour trouver des directions basées sur des paramètres).

Maruthi
la source
C'est un mauvais exemple. Dans ce cas, les directions (toutes les directions possibles, un nombre infini d'entre elles) sont la ressource et les paramètres ne sont que des filtres. Mais vous ne pouvez pas faire un "achat" avec cela, car les filtres n'ont de sens que pour obtenir des opérations et passer une commande ou l'annuler sont des opérations qui changent les données
Tseng
2
achat serait POST to / order avec un json dans le corps pour indiquer qu'une commande est créée. cancel serait PUT to / order avec un json portant le changement d'état de la commande pour indiquer qu'il s'agit d'une mise à jour idempotente. Je suis encore à courir dans une opération qui ne peut pas être exprimée dans un format de ressource.
J'aimerais