REST - mettre les identifiants dans le corps ou non?

96

Disons que je veux avoir une ressource RESTful pour les personnes, où le client peut attribuer un ID.

Une personne ressemble à ceci: {"id": <UUID>, "name": "Jimmy"}

Maintenant, comment le client doit-il le sauvegarder (ou le «PUT»)?

  1. PUT /person/UUID {"id": <UUID>, "name": "Jimmy"} - maintenant nous avons cette mauvaise duplication que nous devons vérifier tout le temps: l'ID dans le corps correspond-il à celui dans le chemin?
  2. Représentation asymétrique:
    • PUT /person/UUID {"name": "Jimmy"}
    • GET /person/UUID Retour {"id": <UUID>, "name": "Jimmy"}
  3. Aucun ID dans le corps - ID uniquement dans l'emplacement:
    • PUT /person/UUID {"name": "Jimmy"}
    • GET /person/UUID Retour {"name": "Jimmy"}
  4. Aucune sorte de ne POSTsemble être une bonne idée puisque l'ID est généré par le client.

Quels sont les schémas courants et les moyens de le résoudre? Les identifiants uniquement localisés semblent être le moyen le plus dogmatiquement correct, mais cela rend également la mise en œuvre pratique plus difficile.

Konrad Garus
la source

Réponses:

62

Il n'y a rien de mal à avoir différents modèles de lecture / écriture: le client peut écrire une représentation de ressource où après le serveur peut renvoyer une autre représentation avec des éléments ajoutés / calculés (ou même une représentation complètement différente - il n'y a rien dans aucune spécification contre cela , la seule exigence est que PUT crée ou remplace la ressource).

Je choisirais donc la solution asymétrique dans (2) et éviterais le "vilain contrôle de duplication" côté serveur lors de l'écriture:

PUT /person/UUID {"name": "Jimmy"}

GET /person/UUID returns {"id": <UUID>, "name": "Jimmy"}
Jørn Wildt
la source
2
Et si vous appliquez la saisie (statique ou dynamique), vous ne pouvez pas avoir de modèles sans ID sans douleur ... Il est donc beaucoup plus facile de supprimer l'ID de l'URL pour les requêtes PUT. Ce ne sera pas "reposant" mais ce sera correct.
Ivan Kleshnin
2
Gardez des TO supplémentaires sans idavec TO avec identifiant et entité et des convertisseurs supplémentaires et une surcharge trop importante pour les programmeurs.
Grigory Kislin
Et si j'obtiens l'ID de BODY ex .: PUT / person {"id": 1, "name": "Jimmy"}. Serait-ce une mauvaise pratique?
Bruno Santos
Mettre la pièce d'identité dans le corps serait très bien. Utilisez un GUID pour l'ID au lieu d'un entier, sinon vous courez le risque de dupliquer les ID.
Jørn Wildt
C'est faux. Voyez ma réponse. PUT doit contenir toute la ressource. Utilisez PATCH si vous souhaitez exclure l'ID et mettre à jour uniquement certaines parties de l'enregistrement.
CompEng88
27

S'il s'agit d'une API publique, vous devez être prudent lorsque vous répondez, mais acceptez généreusement.

Je veux dire par là que vous devriez soutenir à la fois 1 et 2. Je conviens que 3 n'a pas de sens.

Le moyen de prendre en charge à la fois 1 et 2 est d'obtenir l'id de l'url si aucun n'est fourni dans le corps de la requête, et s'il se trouve dans le corps de la requête, alors validez qu'il correspond à l'id dans l'url. Si les deux ne correspondent pas, renvoyez une réponse 400 Bad Request.

Lorsque vous retournez une ressource personne, soyez prudent et incluez toujours l'identifiant dans le json, même s'il est facultatif dans le put.

Jay Pete
la source
3
Cela devrait être la solution acceptée. Une API doit toujours être conviviale. Il devrait être facultatif dans le corps. Je ne devrais pas recevoir d'identifiant d'un POST et ensuite le rendre indéfini dans un PUT. En outre, le point de réponse 400 fait est juste.
Michael le
Environ 400 codes voir également softwareengineering.stackexchange.com/questions/329229/… discussion. En bref, un code 400 n'est pas inapproprié, juste moins spécifique, comparé au 422.
Grigory Kislin
8

Une solution à ce problème implique le concept quelque peu déroutant de «Hypertext As The Engine Of Application State» ou «HATEOAS». Cela signifie qu'une réponse REST contient les ressources disponibles ou les actions à effectuer sous forme de liens hypertexte. En utilisant cette méthode, qui faisait partie de la conception originale de REST, les identifiants / ID uniques des ressources sont eux-mêmes des hyperliens. Ainsi, par exemple, vous pourriez avoir quelque chose comme:

GET /person/<UUID> {"person": {"location": "/person/<UUID>", "data": { "name": "Jimmy"}}}

Ensuite, si vous souhaitez mettre à jour cette ressource, vous pouvez faire (pseudocode):

updatedPerson = person.data
updatedPerson.name = "Timmy"
PUT(URI: response.resource, data: updatedPerson)

Un avantage de ceci est que le client n'a aucune idée de la représentation interne du serveur des ID utilisateur. Les identifiants pourraient changer, et même les URL elles-mêmes pourraient changer, tant que le client a un moyen de les découvrir. Par exemple, lors de l'obtention d'une collection de personnes, vous pouvez renvoyer une réponse comme celle-ci:

GET /people
{ "people": [
    "/person/1",
    "/person/2"
  ]
}

(Vous pouvez, bien sûr, également renvoyer l'objet Personne complète pour chaque personne, en fonction des besoins de l'application).

Avec cette méthode, vous pensez davantage à vos objets en termes de ressources et d'emplacements, et moins en termes d'ID. La représentation interne de l'identifiant unique est ainsi découplée de votre logique client. C'était l'impulsion originale derrière REST: créer des architectures client-serveur plus faiblement couplées que les systèmes RPC qui existaient auparavant, en utilisant les fonctionnalités de HTTP. Pour plus d'informations sur HATEOAS, consultez l'article Wikipédia ainsi que ce court article .

bthecohen
la source
4

Dans une insertion, vous n'avez pas besoin d'ajouter l'ID dans l'URL. De cette façon, si vous envoyez un ID dans un PUT, vous pouvez interpréter comme une MISE À JOUR pour changer la clé primaire.

  1. INSÉRER:

    PUT /persons/ 
      {"id": 1, "name": "Jimmy"}
    HTTP/1.1 201 Created     
      {"id": 1, "name": "Jimmy", "other_field"="filled_by_server"}
    
    GET /persons/1
    
    HTTP/1.1 200 OK
      {"id": 1, "name": "Jimmy", "other_field"="filled_by_server"}  
    
  2. METTRE À JOUR

    PUT /persons/1 
         {"id": "2", "name": "Jimmy Jr"} - 
    HTTP/1.1 200 OK
         {"id": "2", "name": "Jimmy Jr", "other_field"="filled_by_server"}
    
    GET /persons/2 
    
    HTTP/1.1 200 OK
         {"id": "2", "name": "Jimmy Jr", "other_field"="updated_by_server"}
    

L' API JSON utilise cette norme et résout certains problèmes en renvoyant l'objet inséré ou mis à jour avec un lien vers le nouvel objet. Certaines mises à jour ou insertions peuvent inclure une logique métier qui modifiera des champs supplémentaires

Vous verrez également que vous pouvez éviter le get après l'insertion et la mise à jour.

Borjab
la source
3

Cela a déjà été demandé - la discussion vaut le coup d'œil:

Une réponse RESTful GET doit-elle renvoyer l'ID d'une ressource?

C'est l'une de ces questions où il est facile de s'enliser dans un débat sur ce qui est et n'est pas "RESTful" .

Pour ce que ça vaut, j'essaie de penser en termes de ressources cohérentes et de ne pas en changer la conception entre les méthodes. Cependant, à mon humble avis, la chose la plus importante du point de vue de la convivialité est que vous soyez cohérent sur l'ensemble de l'API!

Ben Morris
la source
3

Juste pour info, les réponses ici sont fausses.

Voir:

https://restfulapi.net/rest-api-design-tutorial-with-example/

https://restfulapi.net/rest-put-vs-post/

https://restfulapi.net/http-methods/#patch

METTRE

Utilisez les API PUT principalement pour mettre à jour la ressource existante (si la ressource n'existe pas, l'API peut décider de créer une nouvelle ressource ou non). Si une nouvelle ressource a été créée par l'API PUT, le serveur d'origine DOIT informer l'agent utilisateur via le code de réponse HTTP 201 (Créé) réponse et si une ressource existante est modifiée, soit le 200 (OK) ou 204 (Pas de contenu) des codes de réponse DEVRAIENT être envoyés pour indiquer la réussite de la demande.

Si la demande passe par une antémémoire et que l'URI de demande identifie une ou plusieurs entités actuellement mises en cache, ces entrées DEVRAIENT être traitées comme périmées. Les réponses à cette méthode ne peuvent pas être mises en cache.

Utilisez PUT lorsque vous souhaitez modifier une ressource singulière qui fait déjà partie de la collection de ressources. PUT remplace la ressource dans son intégralité. Utilisez PATCH si la requête met à jour une partie de la ressource.

PIÈCE

Les requêtes HTTP PATCH doivent effectuer une mise à jour partielle sur une ressource. Si vous voyez que les demandes PUT modifient également une entité de ressource afin d'être plus clair - la méthode PATCH est le bon choix pour mettre à jour partiellement une ressource existante et PUT ne doit être utilisée que si vous remplacez une ressource dans son intégralité.

Vous devriez donc l'utiliser de cette manière:

POST    /device-management/devices      : Create a new device
PUT     /device-management/devices/{id} : Update the device information identified by "id"
PATCH   /device-management/devices/{id} : Partial-update the device information identified by "id"

Les pratiques RESTful indiquent que peu importe ce que vous METTEZ à / {id} - le contenu de l'enregistrement doit être mis à jour avec celui fourni par la charge utile - mais GET / {id} doit toujours être lié à la même ressource.

En d'autres termes, PUT / 3 peut mettre à jour l'ID de charge utile à 4, mais GET / 3 doit toujours se lier à la même charge utile (et renvoyer celui dont l'id est défini sur 4).

Si vous décidez que votre API nécessite le même identifiant dans l'URI et la charge utile, c'est votre travail de vous assurer qu'il correspond, mais utilisez certainement PATCH au lieu de PUT si vous excluez l'id dans la charge utile qui devrait être là dans son intégralité . C'est là que la réponse acceptée s'est trompée. PUT doit remplacer la totalité de la ressource, là où le correctif peut être partiel.

CompEng88
la source
2

Bien qu'il soit correct d'avoir différentes représentations pour différentes opérations, une recommandation générale pour PUT est de contenir la charge utile ENTIÈRE . Cela signifie que cela iddevrait être là aussi. Sinon, vous devez utiliser PATCH.

Cela dit, je pense que PUT devrait principalement être utilisé pour les mises à jour et iddevrait toujours être passé dans l'URL. En conséquence, utiliser PUT pour mettre à jour l'identificateur de ressource est une mauvaise idée. Cela nous laisse dans une situation indésirable lorsque idl'URL peut être différente de laid du corps.

Alors, comment résoudre un tel conflit? Nous avons essentiellement 2 options:

  • lancer une exception 4XX
  • ajoutez un en- tête Warning( X-API-Warnetc).

C'est aussi près que possible de répondre à cette question car le sujet en général est une question d'opinion.

yuranos
la source
1

Il n'y a rien de mal à utiliser différentes approches. mais je pense que le meilleur moyen est la solution avec 2nd .

 PUT /person/UUID {"name": "Jimmy"}

 GET /person/UUID returns {"id": <UUID>, "name": "Jimmy"}

il est principalement utilisé de cette manière, même le framework d'entité utilise cette technique lorsque l'entité est ajoutée dans dbContext, la classe sans l'ID généré est l'ID généré par référence dans Entity Framework.

Shan Khan
la source
1

J'examine cela d'un point de vue JSON-LD / Web sémantique, car c'est un bon moyen d'atteindre une véritable conformité REST, comme je l'ai souligné dans ces diapositives . En regardant les choses de ce point de vue, il n'est pas question d'opter pour l'option (1.) car l'ID (IRI) d'une ressource Web doit toujours être égal à l'URL que je peux utiliser pour rechercher / déréférencer la ressource. Je pense que la vérification n'est pas vraiment difficile à mettre en œuvre et qu'elle n'est pas non plus intensive en termes de calcul; donc je ne considère pas cela comme une raison valable pour choisir l'option (2.). Je pense que l'option (3.) n'est pas vraiment une option car POST (create new) a une sémantique différente de PUT (update / replace).

vanthome
la source
0

Vous devrez peut-être examiner les types de requêtes PATCH / PUT.

Les requêtes PATCH sont utilisées pour mettre à jour partiellement une ressource alors que dans les requêtes PUT, vous devez envoyer la ressource entière là où elle est remplacée sur le serveur.

Pour ce qui est d'avoir un identifiant dans l'url, je pense que vous devriez toujours l'avoir car c'est une pratique courante d'identifier une ressource. Même l'API Stripe fonctionne de cette façon.

Vous pouvez utiliser une demande PATCH pour mettre à jour une ressource sur le serveur avec un ID pour l'identifier mais ne mettez pas à jour l'ID réel.

Noman Ur Rehman
la source