Dois-je utiliser PATCH ou PUT dans mon API REST?

274

Je souhaite concevoir mon point de terminaison de repos avec la méthode appropriée pour le scénario suivant.

Il y a un groupe. Chaque groupe a un statut. Le groupe peut être activé ou désactivé par l'administrateur.

Dois-je concevoir mon point final comme

PUT /groups/api/v1/groups/{group id}/status/activate

OU

PATCH /groups/api/v1/groups/{group id}

with request body like 
{action:activate|deactivate}
java_geek
la source
1
Les deux vont bien. Mais jetez un oeil à la RFC pour le format JSON PATCH ( tools.ietf.org/html/rfc6902 ). PATCH s'attend à obtenir une sorte de document diff / patch pour la charge utile (et le JSON brut n'en fait pas partie).
Jørn Wildt
1
@ JørnWildt non, PUT serait un choix horrible. Que mettez-vous là? PATCH est la seule option raisonnable. Eh bien, dans ce cas, vous pouvez utiliser le format PATCH présenté dans la question, et simplement utiliser la méthode PUT; l'exemple PUT est tout simplement faux.
thecoshman
3
Il n'y a rien de mal à exposer une ou plusieurs propriétés en tant que ressources autonomes qu'un client peut obtenir et modifier avec PUT. Mais, oui, l'URL doit alors être / groups / api / v1 / groups / {group id} / status dans lequel vous pouvez METTRE "active" ou "inactive" ou GET pour lire l'état actuel.
Jørn Wildt
3
Voici une bonne explication de la façon dont PATCH doit vraiment être utilisé: williamdurand.fr/2014/02/14/please-do-not-patch-like-an-idiot
rishat
4
" activate" n'est pas une construction RESTful adéquate. Vous essayez probablement de mettre à jour le status"actif" ou le "désactivé". auquel cas vous pouvez PATCHER .../statusavec la chaîne "active" ou "désactivante" dans le corps. Ou si vous essayez de mettre à jour un booléen à status.active, vous pouvez CORRIGER .../status/activeavec le booléen dans le corps
Augie Gardner

Réponses:

328

La PATCHméthode est le bon choix ici lorsque vous mettez à jour une ressource existante - l'ID de groupe. PUTne doit être utilisé que si vous remplacez une ressource dans son intégralité.

De plus amples informations sur la modification partielle des ressources sont disponibles dans la RFC 5789 . Plus précisément, la PUTméthode est décrite comme suit:

Plusieurs applications étendant le protocole HTTP (Hypertext Transfer Protocol) nécessitent une fonctionnalité pour effectuer une modification partielle des ressources. La méthode HTTP PUT existante ne permet que le remplacement complet d'un document. Cette proposition ajoute une nouvelle méthode HTTP, PATCH, pour modifier une ressource HTTP existante.

Luke Peterson
la source
1
Pour être juste, vous pouvez METTRE la chaîne «activer» ou «désactiver» à la ressource. Comme il ne semble y avoir qu'une seule chose à basculer, le remplacer complètement n'est pas si énorme. Et cela permet une demande (insignifiante) plus petite.
thecoshman
35
Il est important de noter que la RFC 5789 est toujours en phase de proposition et n'a pas été officiellement acceptée et est actuellement signalée comme «irrata existent». Cette «meilleure pratique» est très controversée et techniquement, PATCH ne fait pas encore partie de la norme HTTP.
fishpen0
4
Juste mes 2 cents quelques années plus tard: vous pourriez considérer le statut lui-même comme une ressource, et si c'est le cas, l'utilisation de PUT contre / status remplacerait techniquement la ressource de statut à ce point final.
Jono Stewart
3
J'oserais argumenter contre les documents, même si c'est "le" RFC. Les documents indiquent que vous devez utiliser PATCH pour modifier uniquement une partie d'une ressource, mais ils ont omis la chose importante que la méthode PATCH est définie comme une méthode non idempotente. Pourquoi? Si la méthode PUT a été créée avec la mise à jour / remplacement de la ressource entière à l'esprit, alors pourquoi la méthode PATCH n'a-t-elle pas été créée en tant que méthode idempotente comme PUT, si son but était de simplement mettre à jour la partie d'une ressource? Pour moi, cela ressemble plus à une différence d'idempotence de mise à jour, comme "a = 5" (PUT) et "a = a + 5" (PATCH). Les deux peuvent mettre à jour la ressource entière.
Mladen B.
179

Le R dans REST est synonyme de ressource

(Ce qui n'est pas vrai, car il signifie représentationnel, mais c'est une bonne astuce pour se souvenir de l'importance des ressources dans REST).

À propos PUT /groups/api/v1/groups/{group id}/status/activate: vous ne mettez pas à jour un "activer". Un "activer" n'est pas une chose, c'est un verbe. Les verbes ne sont jamais de bonnes ressources. Une règle d'or: si l'action, un verbe, est dans l'URL, elle n'est probablement pas RESTful .

Que fais-tu à la place? Soit vous "ajoutez", "supprimez" ou "mettez à jour" une activation sur un groupe, soit si vous préférez: manipuler une ressource "état" sur un groupe. Personnellement, j'utiliserais des «activations» car elles sont moins ambiguës que le concept de «statut»: la création d'un statut est ambiguë, la création d'une activation ne l'est pas.

  • POST /groups/{group id}/activation Crée (ou demande la création de) une activation.
  • PATCH /groups/{group id}/activationMet à jour certains détails d'une activation existante. Puisqu'un groupe n'a qu'une seule activation, nous savons à quelle ressource d'activation nous faisons référence.
  • PUT /groups/{group id}/activationInsère ou remplace l'ancienne activation. Puisqu'un groupe n'a qu'une seule activation, nous savons à quelle ressource d'activation nous faisons référence.
  • DELETE /groups/{group id}/activation Annulera ou supprimera l'activation.

Ce modèle est utile lorsque «l'activation» d'un groupe a des effets secondaires, tels que des paiements en cours, des courriers envoyés, etc. Seuls POST et PATCH peuvent avoir de tels effets secondaires. Lorsque, par exemple, la suppression d'une activation doit, par exemple, avertir les utilisateurs par courrier électronique, SUPPRIMER n'est pas le bon choix; dans ce cas , vous voulez probablement créer une ressource de désactivation : POST /groups/{group_id}/deactivation.

C'est une bonne idée de suivre ces directives, car ce contrat standard indique très clairement pour vos clients, et toutes les procurations et couches entre le client et vous, sachez quand il est sûr de réessayer et quand ce n'est pas le cas. Disons que le client est quelque part avec un wifi floconneux, et son utilisateur clique sur "désactiver", ce qui déclenche un DELETE: Si cela échoue, le client peut simplement réessayer, jusqu'à ce qu'il obtienne un 404, 200 ou tout autre chose qu'il peut gérer. Mais s'il déclenche un POST to deactivationil sait ne pas réessayer: le POST l'implique.
Tout client a maintenant un contrat qui, une fois suivi, protégera contre l'envoi de 42 e-mails "votre groupe a été désactivé", tout simplement parce que sa bibliothèque HTTP a continué de relancer l'appel vers le backend.

Mise à jour d'un seul attribut: utilisez PATCH

PATCH /groups/{group id}

Si vous souhaitez mettre à jour un attribut. Par exemple, le "statut" pourrait être un attribut sur les groupes qui peut être défini. Un attribut tel que "status" est souvent un bon candidat pour se limiter à une liste blanche de valeurs. Les exemples utilisent un schéma JSON non défini:

PATCH /groups/{group id} { "attributes": { "status": "active" } }
response: 200 OK

PATCH /groups/{group id} { "attributes": { "status": "deleted" } }
response: 406 Not Acceptable

Remplacer la ressource, sans effets secondaires, utilisez PUT.

PUT /groups/{group id}

Si vous souhaitez remplacer un groupe entier. Cela ne signifie pas nécessairement que le serveur crée réellement un nouveau groupe et rejette l'ancien, par exemple les identifiants peuvent rester les mêmes. Mais pour les clients, c'est ce que PUT peut signifier: le client doit supposer qu'il obtient un élément entièrement nouveau, basé sur la réponse du serveur.

En cas de PUTdemande, le client doit toujours envoyer l'intégralité de la ressource, avec toutes les données nécessaires à la création d'un nouvel élément: généralement les mêmes données que celles nécessaires à une création POST.

PUT /groups/{group id} { "attributes": { "status": "active" } }
response: 406 Not Acceptable

PUT /groups/{group id} { "attributes": { "name": .... etc. "status": "active" } }
response: 201 Created or 200 OK, depending on whether we made a new one.

Une exigence très importante PUTest idempotente: si vous avez besoin d'effets secondaires lors de la mise à jour d'un groupe (ou de la modification d'une activation), vous devez l'utiliser PATCH. Donc, lorsque la mise à jour se traduit par exemple par l'envoi d'un mail, ne l'utilisez pas PUT.

berkes
la source
3
Ce fut très instructif pour moi. "Ce modèle est utile lorsque l '" activation "d'un groupe a des effets secondaires" - Comment se fait-il que ce modèle soit utile, en particulier en ce qui concerne les actions ayant des effets secondaires, par opposition aux points finaux initiaux du PO
Abdul
1
@Abdul, le modèle est utile pour de nombreuses raisons, mais pour les effets secondaires, il devrait être très clair pour le client, quels effets a une action. Lorsque, par exemple, une application iOS décide d'envoyer l'intégralité du carnet d'adresses en tant que "contacts", il devrait être extrêmement clair quels effets secondaires la création, la mise à jour, la suppression, etc., d'un contact a. Pour éviter l'envoi de masse de tous les contacts, par exemple.
berkes
1
Dans RESTfull, PUT peut également changer l'identité des entités - par exemple l'ID PrimaryKey où il pourrait entraîner l'échec d'une demande parallèle. (par exemple, la mise à jour de l'entité entière doit supprimer certaines lignes et en ajouter de nouvelles, créant ainsi de nouvelles entités) Où PATCH ne doit jamais être en mesure de le faire, permettant un nombre illimité de demandes de PATCH sans affecter d'autres "applications"
Piotr Kula
1
Réponse très utile. Merci! J'ajouterais également un commentaire, tout comme dans la réponse de Luke, soulignant que la différence entre PUT / PATCH n'est pas seulement la mise à jour complète / partielle, c'est aussi l'idempotence qui est différente. Ce n'était pas une erreur, c'était une décision intentionnelle et je pense que peu de gens prennent cela en considération lors du choix de l'utilisation de la méthode HTTP.
Mladen B.
1
Les services @richremer, comme les modèles, sont des abstractions internes. Tout comme c'est une abstraction médiocre d'exiger une relation 1-1 entre les modèles de points de terminaison REST et ORM ou même les tables de base de données, c'est une abstraction médiocre d'exposer les services. L'extérieur, votre API, doit communiquer des modèles de domaine. La façon dont vous les implémentez en interne ne concerne pas l'API. Vous devriez être libre de passer d'un ActivationService à un flux d'activation basé sur CQRS, sans avoir à changer votre API.
berkes
12

Je recommanderais d'utiliser PATCH, car votre «groupe» de ressources a de nombreuses propriétés mais dans ce cas, vous ne mettez à jour que le champ d'activation (modification partielle)

selon la RFC5789 ( https://tools.ietf.org/html/rfc5789 )

La méthode HTTP PUT existante ne permet que le remplacement complet d'un document. Cette proposition ajoute une nouvelle méthode HTTP, PATCH, pour modifier une ressource HTTP existante.

De plus, plus en détail,

La différence entre les demandes PUT et PATCH se reflète dans la façon dont le serveur traite l'entité incluse pour modifier la ressource
identifiée par l'URI de demande. Dans une demande PUT, l'entité incluse est considérée comme une version modifiée de la ressource stockée sur le
serveur d'origine et le client demande que la version stockée
soit remplacée. Avec PATCH, cependant, l'entité incluse contient un ensemble d'instructions décrivant comment une ressource résidant actuellement sur le
serveur d'origine doit être modifiée pour produire une nouvelle version. La méthode PATCH affecte la ressource identifiée par l'URI de demande, et elle
PEUT également avoir des effets secondaires sur d'autres ressources; c.-à-d. de nouvelles ressources
peuvent être créés, ou ceux existants modifiés, par l'application d'un
PATCH.

PATCH n'est ni sûr ni idempotent tel que défini par [RFC2616], Section 9.1.

Les clients doivent choisir quand utiliser PATCH plutôt que PUT. Par
exemple, si la taille du document de correctif est supérieure à la taille des
nouvelles données de ressource qui seraient utilisées dans un PUT, il peut être
judicieux d'utiliser PUT au lieu de PATCH. Une comparaison avec le POST est encore plus difficile, car le POST est utilisé de manière très variable et peut
englober des opérations de type PUT et PATCH si le serveur le souhaite. Si
l'opération ne modifie pas la ressource identifiée par l'URI de demande de manière prévisible, le POST doit être considéré au lieu du PATCH
ou du PUT.

Le code de réponse pour PATCH est

Le code de réponse 204 est utilisé car la réponse ne porte pas de corps de message (qu'une réponse avec le code 200 aurait). Notez que d'autres codes de réussite pourraient également être utilisés.

référez-vous également à http://restcookbook.com/HTTP%20Methods/patch/

Attention: une API implémentant PATCH doit patcher atomiquement. Il NE DOIT PAS être possible que les ressources soient à moitié corrigées à la demande d'un GET.

Clojurevangelist
la source
7

Puisque vous souhaitez concevoir une API en utilisant le style architectural REST, vous devez penser à vos cas d'utilisation pour décider quels concepts sont suffisamment importants pour être exposés en tant que ressources. Si vous décidez d'exposer le statut d'un groupe en tant que sous-ressource, vous pouvez lui donner l'URI suivant et implémenter la prise en charge des méthodes GET et PUT:

/groups/api/groups/{group id}/status

L'inconvénient de cette approche sur PATCH pour la modification est que vous ne pourrez pas modifier plus d'une propriété d'un groupe de manière atomique et transactionnelle. Si les modifications transactionnelles sont importantes, utilisez PATCH.

Si vous décidez d'exposer le statut en tant que sous-ressource d'un groupe, il doit s'agir d'un lien dans la représentation du groupe. Par exemple, si l'agent obtient le groupe 123 et accepte XML, le corps de la réponse peut contenir:

<group id="123">
  <status>Active</status>
  <link rel="/linkrels/groups/status" uri="/groups/api/groups/123/status"/>
  ...
</group>

Un lien hypertexte est nécessaire pour remplir l' hypermédia en tant que moteur de la condition d'état d' application du style architectural REST.

Andrew Dobrowolski
la source
0

Je préfère généralement quelque chose d'un peu plus simple, comme activate/ deactivatesub-resource (lié par un en- Linktête avec rel=service).

POST /groups/api/v1/groups/{group id}/activate

ou

POST /groups/api/v1/groups/{group id}/deactivate

Pour le consommateur, cette interface est extrêmement simple, et elle suit les principes REST sans vous enliser dans la conceptualisation des "activations" en tant que ressources individuelles.

riche souvenir
la source
0

Une option possible pour implémenter un tel comportement est

PUT /groups/api/v1/groups/{group id}/status
{
    "Status":"Activated"
}

Et évidemment, si quelqu'un a besoin de le désactiver, PUT aura le Deactivatedstatut JSON.

En cas de nécessité d'activation / désactivation de masse, PATCHpeut entrer dans le jeu (pas pour le groupe exact, mais pour la groupsressource:

PATCH /groups/api/v1/groups
{
    { “op”: “replace”, “path”: “/group1/status”, “value”: “Activated” },
    { “op”: “replace”, “path”: “/group7/status”, “value”: “Activated” },
    { “op”: “replace”, “path”: “/group9/status”, “value”: “Deactivated” }
}

En général, c'est l'idée comme @Andrew Dobrowolski le suggère, mais avec de légers changements dans la réalisation exacte.

Ivan Sokalskiy
la source