API REST - Pourquoi utiliser PUT DELETE POST GET?

155

Donc, je regardais à travers quelques articles sur la création d'API REST. Et certains d'entre eux suggèrent d'utiliser tous les types de requêtes HTTP: comme PUT DELETE POST GET. Nous créerions par exemple index.php et écririons l'API de cette façon:

$method = $_SERVER['REQUEST_METHOD'];
$request = split("/", substr(@$_SERVER['PATH_INFO'], 1));

switch ($method) {
  case 'PUT':
    ....some put action.... 
    break;
  case 'POST':
    ....some post action.... 
    break;
  case 'GET':
    ....some get action.... 
    break;
  case 'DELETE':
    ....some delete action.... 
    break;
}

OK, d'accord - je ne sais pas (encore) grand chose sur les services Web. Mais, ne serait-il pas plus facile d'accepter simplement un objet JSON via regular POSTor GET(qui contiendrait le nom de la méthode et tous les paramètres), puis de répondre également en JSON. Nous pouvons facilement linéariser / désérialiser via PHP de json_encode()et json_decode()et faire tout ce que nous voulons avec ces données sans avoir à traiter avec différentes méthodes de requête HTTP.

Est-ce que je manque quelque chose?

MISE À JOUR 1:

Ok - après avoir fouillé dans diverses API et beaucoup appris sur XML-RPC , JSON-RPC , SOAP , REST, je suis arrivé à la conclusion que ce type d'API est solide. En fait, l'échange de pile utilise à peu près cette approche sur leurs sites et je pense que ces personnes savent ce qu'elles font de l'API Stack Exchange .

Stann
la source
4
Pourquoi forcer une charge utile JSON? Et s'il n'y a pas de JSON et que c'est un simple GET?
Mike DeSimone
1
Oui. w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9
Ignacio Vazquez-Abrams

Réponses:

200

L'idée de RE présentation S tate T ransfert ne est pas sur l' accès aux données de la manière la plus simple possible.

Vous avez suggéré d'utiliser des demandes de publication pour accéder à JSON, qui est un moyen parfaitement valide d'accéder / manipuler des données.

REST est une méthodologie pour un accès significatif aux données. Lorsque vous voyez une demande dans REST, ce qui se passe avec les données doit immédiatement apparaître.

Par exemple:

GET: /cars/make/chevrolet

va probablement retourner une liste de voitures chevy. Une bonne API REST pourrait même incorporer certaines options de sortie dans la chaîne de requête comme ?output=jsonou ?output=htmlqui permettraient à l'accesseur de décider dans quel format les informations doivent être encodées.

Après un peu de réflexion sur la façon de typage des données raisonnablement insertion dans un API REST, j'ai conclu que la meilleure façon de spécifier le type de données serait explicitement être via l'extension de fichier déjà existant tel que .js, .json, .htmlou .xml. Une extension de fichier manquante prendrait par défaut le format par défaut (tel que JSON); une extension de fichier non prise en charge pourrait renvoyer un 501 Not Implementedcode d'état .

Un autre exemple:

POST: /cars/
{ make:chevrolet, model:malibu, colors:[red, green, blue, grey] }

va probablement créer un nouveau chevy malibu dans la base de données avec les couleurs associées. Je dis probablement que l'API REST n'a pas besoin d'être directement liée à la structure de la base de données. C'est juste une interface de masquage pour que les vraies données soient protégées (pensez-y comme des accesseurs et des mutateurs pour une structure de base de données).

Nous devons maintenant passer à la question de l' idempotence . Habituellement, REST implémente CRUD sur HTTP. HTTP utilise GET, PUT, POSTet DELETEpour les demandes.

Une implémentation très simpliste de REST pourrait utiliser le mappage CRUD suivant:

Create -> Post
Read   -> Get
Update -> Put
Delete -> Delete

Il y a un problème avec cette implémentation: Post est défini comme une méthode non idempotente. Cela signifie que les appels ultérieurs de la même méthode Post entraîneront des états de serveur différents . Get, Put et Delete sont idempotents; ce qui signifie que les appeler plusieurs fois devrait entraîner un état de serveur identique.

Cela signifie qu'une demande telle que:

Delete: /cars/oldest

pourrait en fait être implémenté comme:

Post: /cars/oldest?action=delete

Tandis que

Delete: /cars/id/123456

entraînera le même état de serveur si vous l'appelez une fois, ou si vous l'appelez 1000 fois.

Une meilleure façon de gérer le retrait de l' oldestarticle serait de demander:

Get: /cars/oldest

et utilisez les IDdonnées résultantes pour faire une deletedemande:

Delete: /cars/id/[oldest id]

Un problème avec cette méthode serait si un autre /carsélément était ajouté entre le moment où a /oldestété demandé et le moment où le a deleteété émis.

zzzzBov
la source
3
@Andre c'est une combinaison d'un certain nombre de raisons: suivre les directives HTTP signifie que vous aurez (probablement) moins de problèmes de compatibilité descendante lorsque les choses changent; l'utilisation d'un formulaire html via POST avertira l'utilisateur pour plusieurs soumissions des mêmes données (ceci afin d'éviter une transaction non idempotente); suivre une meilleure pratique bien définie est, bien..la meilleure pratique. Le repos n'est pas défini avec une implémentation spécifique à l'esprit, ce qui vous permet de l'utiliser comme bon vous semble. Je suggérerais de tirer parti de tous les codes d'erreur et méthodes de requête HTTP, mais vous êtes autorisé à le faire comme vous le souhaitez
zzzzBov
4
Donc, le problème avec cette réponse (c'est une réponse décente, mais pas complète) est qu'elle ne répond pas à la question principale qu'il a posée: pourquoi vous utiliseriez des verbes HTTP et l'URI plutôt que des données JSON personnalisées (peut-être une sorte de Syntaxe d'appel d'API basée sur JSON). Vous pouvez créer votre syntaxe JSON personnalisée pour qu'elle soit "immédiatement ... apparente ce qui se passe avec les données". Ce que vous ne pouvez pas faire, c'est facilement utiliser les fonctionnalités intégrées et les couches réseau en plus de HTTP, comme vous le pouvez avec une API qui suit toutes les conventions REST. Non pas que ma réponse soit parfaite, bien sûr;)
Merlyn Morgan-Graham
4
@Andre: Les exemples utilisés par l'entrée wiki sont l'authentification, la mise en cache et la négociation du type de contenu. Maintenant que j'y pense plus, vous pourrez peut-être les utiliser avec des interfaces de style RPC, mais la tentation sera souvent d'implémenter votre propre système à partir de zéro, ou de coder une intégration à un système existant. Avec REST, vous pouvez utiliser l'intégration intégrée et l'administrer sur le serveur Web. Cela signifie un couplage plus lâche, ce qui signifie que vous devez implémenter moins, et signifie que votre application est beaucoup plus flexible pour modifier les options à l'avenir avec moins de code et d'impact de test.
Merlyn Morgan-Graham le
10
Au lieu de DELETE: / cars / plus anciens, que diriez-vous de GET: / cars / plus anciens suivi d'un DELETE? De cette façon, vous avez deux commandes idempotentes séparément.
Neil
3
+1; Je suis d'accord que c'est une bonne réponse (j'y reviens pour le plaisir et le profit). POST: /cars/oldestremplacer un DELETE n'a pas beaucoup de sens. Quelque chose comme - POST: /cars/oldest/deletepourrait, mais je pense que j'aime mieux la solution de Neil. Le seul avantage qu'une suppression directe donne sur sa solution get-id-delete-id est l'atomicité. Je voudrais une justification commerciale claire avec un scénario non artificiel avant de mettre en œuvre une telle chose. Vous n'avez pas besoin de prendre en charge tous les verbes sur tous les objets / URL.
Merlyn Morgan-Graham
39

C'est une question de sécurité et de maintenabilité.

méthodes sûres

Dans la mesure du possible, vous devez utiliser des méthodes «sûres» (unidirectionnelles) telles que GET et HEAD afin de limiter les vulnérabilités potentielles.

méthodes idempotentes

Dans la mesure du possible, vous devez utiliser des méthodes «idempotentes» telles que GET, HEAD, PUT et DELETE, qui ne peuvent pas avoir d'effets secondaires et sont donc moins sujettes aux erreurs / plus faciles à contrôler.

La source

Markus
la source
1
Désolé, mais en quoi les méthodes PUT et DELETE sont-elles idempotentes? Ils affectent l'état du serveur et ses données!
Mahmoud Al-Qudsi
27
@Ordinateur: Faire le même PUT ou le même DELETE aboutit au même état final. C'est ce que signifie «idempotent».
Ignacio Vazquez-Abrams
4
Pour plus de précision: une opération F est idempotente, si sa seule application et ses plusieurs applications consécutives renvoient toutes deux le même résultat. Plus précisément F est idempotent si et seulement si F (x) = F (F (x)). Par exemple, Supprimer est idempotent, car lorsque vous supprimez un élément une fois ou que vous le supprimez plusieurs fois, le résultat est le même: l'élément est supprimé une seule fois avec la première application de suppression et rien ne se passe dans la deuxième ou la troisième application de suppression.
qartal
1
Mais en termes de création, lorsque vous créez un nouvel enregistrement avec une commande create et émettez à nouveau la même commande, deux enregistrements sont (probablement) créés (bien que les deux reflètent les mêmes informations).
qartal
qartal - votre définition fonctionnelle pour idempotent devrait être 'F (X) = F (X) F (X)'. Belle façon de le formuler cependant.
Gerard ONeill
26

En bref, REST met l'accent sur les noms sur les verbes. Au fur et à mesure que votre API devient plus complexe, vous ajoutez plus de choses, plutôt que plus de commandes.

Neil
la source
2
J'ai eu un peu de mal à comprendre cela. Ce message ( lornajane.net/posts/2013/… ) selon lequel le verbe devrait provenir de la requête HTTP afin que l'URI ne contienne alors que des noms l'a effacé un peu pour moi
icc97
9

Vous avez demandé :

ne serait-il pas plus facile d'accepter simplement un objet JSON via $ _POST normal, puis de répondre également en JSON

À partir de Wikipedia sur REST :

Les applications RESTful maximisent l'utilisation de l'interface préexistante et bien définie et des autres capacités intégrées fournies par le protocole réseau choisi, et minimisent l'ajout de nouvelles fonctionnalités spécifiques à l'application en plus de celui-ci

D'après ce que (peu) j'ai vu, je pense que cela est généralement accompli en maximisant l'utilisation des verbes HTTP existants et en concevant un schéma d'URL pour votre service qui soit aussi puissant et évident que possible.

Les protocoles de données personnalisés (même s'ils sont construits sur des protocoles standard, tels que SOAP ou JSON) sont déconseillés et doivent être minimisés pour se conformer au mieux à l'idéologie REST.

SOAP RPC sur HTTP, par contre, encourage chaque concepteur d'application à définir un nouveau vocabulaire arbitraire de noms et de verbes (par exemple getUsers (), savePurchaseOrder (...)), généralement superposé au verbe HTTP 'POST'. Cela ne tient pas compte de nombreuses fonctionnalités existantes de HTTP telles que l'authentification, la mise en cache et la négociation du type de contenu, et peut laisser le concepteur de l'application réinventer plusieurs de ces fonctionnalités dans le nouveau vocabulaire.

Les objets réels avec lesquels vous travaillez peuvent être dans n'importe quel format. L'idée est de réutiliser autant de HTTP que possible pour exposer vos opérations que l'utilisateur souhaite effectuer sur ces ressources (requêtes, gestion / mutation d'état, suppression).

Vous avez demandé :

Est-ce que je manque quelque chose?

Il y a beaucoup plus à savoir sur REST et la syntaxe URI / les verbes HTTP eux-mêmes. Par exemple, certains verbes sont idempotents, d'autres non. Je n'ai rien vu à ce sujet dans votre question, alors je n'ai pas pris la peine d'essayer de m'y plonger. Les autres réponses et Wikipedia ont tous deux beaucoup de bonnes informations.

En outre, il y a beaucoup à apprendre sur les différentes technologies de réseau construites sur HTTP dont vous pouvez tirer parti si vous utilisez une API vraiment reposante. Je commencerais par l'authentification.

Merlyn Morgan-Graham
la source
8

En ce qui concerne l'utilisation de l'extension pour définir le type de données. J'ai remarqué que l'API MailChimp le faisait, mais je ne pense pas que ce soit une bonne idée.

GET /zzz/cars.json/1

GET /zzz/cars.xml/1

Cela semble être une bonne idée, mais je pense que l’approche «plus ancienne» est meilleure - en utilisant les en-têtes HTTP

GET /xxx/cars/1
Accept: application/json

De plus, les en-têtes HTTP sont bien meilleurs pour la communication de types de données croisées (si jamais quelqu'un en aurait besoin)

POST /zzz/cars
Content-Type: application/xml     <--- indicates we sent XML to server
Accept: application/json          <--- indicates we want get data back in JSON format  
Pawel Cioch
la source
4

Est-ce que je manque quelque chose?

Oui. ;-)

Ce phénomène existe en raison de la contrainte d'interface uniforme . REST aime utiliser des normes déjà existantes au lieu de réinventer la roue. La norme HTTP s'est déjà avérée hautement évolutive (le Web fonctionne depuis un certain temps). Pourquoi devrions-nous réparer quelque chose qui n'est pas cassé?!

Remarque: la contrainte d'interface uniforme est importante si vous souhaitez découpler les clients du service. Cela revient à définir des interfaces pour les classes afin de les découpler les unes des autres. Ofc. ici, l'interface uniforme se compose de normes telles que HTTP , types MIME , URI , RDF , vocabulaires de données liées , vocabulaire hydra , etc.

inf3rno
la source
2

Une bonne sémantique est importante dans la programmation.

Utiliser plus de méthodes en plus de GET / POST sera utile car cela augmentera la lisibilité de votre code et le rendra plus facile à maintenir.

Pourquoi?

Parce que vous savez que GET récupérera les données de votre API. Vous savez que POST ajoutera de nouvelles données à votre système. Vous savez que PUT fera des mises à jour. DELETE supprimera les lignes, etc., etc.

Normalement, je structure mes services Web RESTFUL de manière à ce qu'un rappel de fonction soit nommé de la même manière que la méthode.

J'utilise PHP, donc j'utilise function_exists (je pense que c'est appelé). Si la fonction n'existe pas, je lance un 405 (METHODE NON AUTORISEE).

HumbleWebDev
la source
2

Bill Venners: Dans votre article de blog intitulé "Pourquoi REST a échoué", vous avez dit que nous avons besoin des quatre verbes HTTP - GET, POST, PUT et DELETE - et vous avez déploré que les fournisseurs de navigateurs se contentent de GET et POST. "Pourquoi avons-nous besoin des quatre Pourquoi ne sont pas assez GET et POST?

Elliotte Rusty Harold: Il existe quatre méthodes de base dans HTTP: GET, POST, PUT et DELETE. GET est utilisé la plupart du temps. Il est utilisé pour tout ce qui est sûr, qui ne provoque aucun effet secondaire. GET peut être mis en signet, mis en cache, lié à, transmis via un serveur proxy. C'est une opération très puissante, une opération très utile.

POST, en revanche, est peut-être l'opération la plus puissante. Il peut tout faire. Il n'y a pas de limites à ce qui peut arriver, et par conséquent, vous devez être très prudent avec cela. Vous ne le signalez pas. Vous ne le cachez pas. Vous ne le pré-récupérez pas. Vous ne faites rien avec un POST sans demander à l'utilisateur. Voulez-vous faire cela? Si l'utilisateur appuie sur le bouton, vous pouvez POSTER du contenu. Mais vous n'allez pas regarder tous les boutons d'une page et commencer à les appuyer au hasard. En revanche, les navigateurs peuvent consulter tous les liens de la page et les pré-extraire, ou pré-récupérer ceux qu'ils pensent être les plus susceptibles d'être suivis. Et en fait, certains navigateurs et extensions Firefox et divers autres outils ont essayé de le faire à un moment ou à un autre.

PUT et DELETE sont au milieu entre GET et POST. La différence entre PUT ou DELETE et POST est que PUT et DELETE sont * idempotents, alors que POST ne l'est pas. PUT et DELETE peuvent être répétés si nécessaire. Supposons que vous essayez de télécharger une nouvelle page sur un site. Supposons que vous souhaitiez créer une nouvelle page à l' adresse http://www.example.com/foo.html, donc vous tapez votre contenu et vous le METTEZ à cette URL. Le serveur crée cette page à cette URL que vous fournissez. Maintenant, supposons que pour une raison quelconque, votre connexion réseau tombe en panne. Vous n'êtes pas sûr, la demande a-t-elle été acceptée ou non? Peut-être que le réseau est lent. Il y avait peut-être un problème de serveur proxy. Il est donc parfaitement normal de réessayer, ou encore - autant de fois que vous le souhaitez. Parce que METTRE le même document à la même URL dix fois ne sera pas différent de le mettre une fois. La même chose est vraie pour DELETE. Vous pouvez SUPPRIMER quelque chose dix fois, et cela revient à le supprimer une fois.

En revanche, POST peut provoquer à chaque fois quelque chose de différent. Imaginez que vous sortez d'une boutique en ligne en appuyant sur le bouton d'achat. Si vous envoyez à nouveau cette demande POST, vous pourriez finir par acheter tout ce qui se trouve dans votre panier une deuxième fois. Si vous l'envoyez à nouveau, vous l'avez acheté une troisième fois. C'est pourquoi les navigateurs doivent faire très attention à ne pas répéter les opérations POST sans le consentement explicite de l'utilisateur, car POST peut provoquer deux choses si vous le faites deux fois, trois choses si vous le faites trois fois. Avec PUT et DELETE, il y a une grande différence entre zéro demande et une, mais il n'y a pas de différence entre une demande et dix.

Veuillez visiter l'URL pour plus de détails. http://www.artima.com/lejava/articles/why_put_and_delete.html

Mettre à jour:

Méthodes idempotentes Une méthode HTTP idempotente est une méthode HTTP qui peut être appelée plusieurs fois sans résultats différents. Peu importe que la méthode soit appelée une seule fois ou dix fois. Le résultat devrait être le même. Encore une fois, cela ne s'applique qu'au résultat, pas à la ressource elle-même. Cela peut toujours être manipulé (comme un horodatage de mise à jour, à condition que ces informations ne soient pas partagées dans la représentation (actuelle) des ressources.

Considérez les exemples suivants:

a = 4;

a ++;

Le premier exemple est idempotent: peu importe le nombre de fois que nous exécutons cette instruction, a sera toujours 4. Le deuxième exemple n'est pas idempotent. L'exécution de cela 10 fois entraînera un résultat différent de celui exécuté 5 fois. Étant donné que les deux exemples changent la valeur de a, les deux ne sont pas des méthodes sûres.

Bimal Das
la source
1
A propos de l'exemple d'une nouvelle page, POST ne devrait-il pas être utilisé de cette manière, alors que PUT pour une mise à jour? La création d'une nouvelle page est un processus qui produit un nouveau résultat à chaque fois, tandis que la même modification peut être répétée à tout moment, criant le même résultat à chaque fois. Belle formulation et explication, cependant.
Spyryto
0

Fondamentalement, REST est ( wiki ):

  1. Architecture client-serveur
  2. Apatridie
  3. Cachéabilité
  4. Système en couches
  5. Code à la demande (facultatif)
  6. Interface uniforme

REST n'est pas un protocole, ce sont des principes. Différents uris et méthodes - quelqu'un que l'on appelle les meilleures pratiques.

MAX
la source