Méthode RESTful pour créer plusieurs éléments en une seule demande

122

Je travaille sur un petit programme client-serveur pour collecter les commandes. Je veux faire cela de manière "REST (ful)".

Ce que je veux faire, c'est:

Collecter toutes les lignes de commande (produit et quantité) et envoyer la commande complète au serveur

Pour le moment, je vois deux options pour le faire:

  1. Envoyez chaque ligne de commande au serveur: POST qty et product_id

En fait, je ne veux pas faire cela parce que je veux limiter le nombre de requêtes au serveur, donc option 2:

  1. Collectez toutes les lignes de commande et envoyez-les au serveur à la fois.

Comment dois-je mettre en œuvre l'option 2? Voici quelques idées que j'ai: enveloppez toutes les lignes de commande dans un objet JSON et envoyez-le au serveur ou utilisez un tableau pour publier les lignes de commande.

Est-ce une bonne idée ou une bonne pratique de mettre en œuvre l'option 2, et si oui, comment dois-je le faire?

Qu'est-ce qu'une bonne pratique?


la source

Réponses:

74

Je crois qu'une autre manière correcte d'aborder cela serait de créer une autre ressource qui représente votre collection de ressources. Exemple, imaginez que nous avons un point de terminaison comme /api/sheep/{id}et que nous pouvons POSTER /api/sheeppour créer une ressource mouton.

Maintenant, si nous voulons prendre en charge la création en bloc, nous devrions envisager une nouvelle ressource de flock à /api/flock(ou /api/<your-resource>-collections'il vous manque un nom plus significatif). N'oubliez pas que les ressources n'ont pas besoin d'être mappées à votre base de données ou aux modèles d'application . C'est une idée fausse courante.

Les ressources sont une représentation de plus haut niveau, sans rapport avec vos données. Opérer sur une ressource peut avoir des effets secondaires importants, comme déclencher une alerte à un utilisateur, mettre à jour d'autres données associées, lancer un processus de longue durée, etc. Par exemple, nous pourrions mapper un système de fichiers ou même la pscommande unix en API REST.

Je pense qu'il est prudent de supposer que l'exploitation d'une ressource peut également signifier la création de plusieurs autres entités comme effet secondaire.

miguelcobain
la source
Je suis d'accord avec cela. Vous devez faire abstraction du concept de collection de votre ressource et la traiter comme une ressource. Cela vous donnera également plus de flexibilité à l'avenir, lorsque vous voudrez commencer à faire des opérations sur cela, etc.
villy393
C'est la bonne approche. Cela ne rompt pas la demande de collecte POST. Depuis, il est utilisé pour publier une seule entité. L'envoi d'une demande groupée avec une "entité groupée distincte" est la bonne approche.
Trieur
2
J'aime tellement votre nom de point final d'API avec les moutons et le troupeau! Avec un certain degré d'abstraction, il a presque une référence biblique: "J'ai d'autres moutons, qui ne sont pas de cette bergerie; je dois les amener aussi, et ils entendront ma voix; et ils deviendront un troupeau avec un berger." Jean 10:16.
Evgeny
1
Fait intéressant, les gens recommandent d'utiliser la forme plurielle (de la collection) dans l'URL lorsque vous souhaitez créer une seule ressource, comme ceci: envoyez un POST à ​​/ api / books pour créer un livre. Mais alors, lorsque vous souhaitez créer 100 livres (en une seule demande en tant que json), sur quelle URL publieriez-vous la collection de 100 livres? c'est là que commence l'agitation.
code4kix
@ code4kix que vous pourriez utiliser /api/book-group, /api/book-collectionou quelque chose de similaire.
miguelcobain
46

Bien que les opérations en bloc (par exemple, la création par lots) soient essentielles dans de nombreux systèmes, elles ne sont pas formellement traitées par le style d'architecture RESTful.

J'ai trouvé que publier une collection comme vous l'avez suggéré fonctionne essentiellement, mais des problèmes surviennent lorsque vous devez signaler des échecs en réponse à une telle demande. Ces problèmes sont pires lorsque plusieurs échecs se produisent pour différentes causes ou lorsque le serveur ne prend pas en charge les transactions. Je vous suggère que s'il n'y a pas de problème de performances, par exemple lorsque le fournisseur de services est sur le LAN (pas sur le WAN) ou que les données sont relativement petites, cela vaut la peine d'envoyer 100 requêtes POST au serveur. Restez simple, commencez par des demandes distinctes et si vous avez un problème de performance, essayez d'optimiser.

LiorH
la source
3
Avez-vous trouvé vous-même une solution aux erreurs en cas de batching? Sur une connexion mobile, l'envoi de 100 demandes de publication pour afficher une page semble être une mauvaise idée.
Thomas Ahle
J'ajoute les erreurs à un tableau, je dirige l'utilisateur vers une page d'erreur 419 Conflict (et je renvoie cette erreur au client) et j'affiche le tableau des erreurs. Voir ma réponse ci-dessous pour plus de détails.
Eric Fuller
5
Ça n'a pas de sens. La question est d'envoyer une commande pour de nombreux articles, ce qui, comme beaucoup l'ont dit, vous pouvez simplement dans l'entité d'une demande POST. La façon dont le serveur gère cela est tout autre chose. Dans ce cas, je ne vois aucun problème avec la création d'une commande, le remplissage de ce que vous pouvez pour cette commande et le remplissage de détails qui n'ont pas pu être exécutés. De cette façon, un utilisateur peut alors voir sa commande et voir que tous les articles sauf N ont été ajoutés à la commande, mais que certains étaient en rupture de stock ou que le système ne savait pas quoi faire. Une autre option plus simple mais moins conviviale consiste à tout rejeter
thecoshman
2
@thecoshman a beaucoup changé en 3,25 ans. Vous devriez probablement publier une réponse complètement formulée à la question.
dlamblin
3
@dlamblin ouais, je devrais probablement faire beaucoup de choses ... J'y reviendrai peut-être à un moment donné ...
thecoshman
9

Facebook explique comment faire cela: https://developers.facebook.com/docs/graph-api/making-multiple-requests

Demandes groupées simples

L'API batch prend dans un tableau de requêtes HTTP logiques représentées sous forme de tableaux JSON - chaque requête a une méthode (correspondant à la méthode HTTP GET / PUT / POST / DELETE etc.), une relative_url (la partie de l'URL après graph.facebook. com), un tableau d'en-têtes optionnel (correspondant aux en-têtes HTTP) et un corps optionnel (pour les requêtes POST et PUT). L'API Batch renvoie un tableau de réponses HTTP logiques représentées sous forme de tableaux JSON - chaque réponse a un code d'état, un tableau d'en-têtes facultatif et un corps facultatif (qui est une chaîne codée JSON).

rwitzel
la source
1
C'est un lien très intéressant, la solution proposée me semble utilisable. Quoi qu'il en soit, chez StackOverflow, la réponse préférée est d'expliquer le concept de la solution dans le corps d'une réponse car les liens peuvent changer ou disparaître.
Jan Vlcinsky
7
C'est vraiment la façon de faire de Facebook, pas nécessairement RESTful comme le PO l'a demandé
0cd
Je pense qu'une API Batch (de Google, Facebook, etc. - @PuneetArora) est plus utile lors du regroupement de plusieurs demandes non liées. Créer une requête qui crée un élément, puis regrouper toutes ces requêtes pour envoyer une collection d'éléments est de la "folie" (Einstein). Créez simplement une requête qui transmet une collection d'éléments.
tfmontague
8

Votre idée me semble valable. La mise en œuvre est une question de votre préférence. Vous pouvez utiliser JSON ou simplement des paramètres pour cela (tableau "order_lines []") et faire

POST /orders

Puisque vous allez créer plus de ressources à la fois en une seule action (commande et ses lignes), il est vital de valider chacune d'entre elles et de les sauvegarder uniquement si elles réussissent toutes la validation, c'est-à-dire. vous devriez le faire dans une transaction.

Milan Novota
la source
6

Je suppose qu'il est préférable d'envoyer des demandes séparées dans une seule connexion . Bien sûr, votre serveur Web doit le prendre en charge

zakovyrya
la source
5

En fait, je me suis battu avec ça ces derniers temps, et voici ce vers quoi je travaille.

Si un POST qui ajoute plusieurs ressources réussit, renvoyez un 200 OK (j'envisageais un 201, mais l'utilisateur n'atterrit finalement pas sur une ressource qui a été créée) avec une page qui affiche toutes les ressources qui ont été ajoutées, soit en lecture -seulement ou mode modifiable. Par exemple, un utilisateur peut sélectionner et POST plusieurs images dans une galerie en utilisant un formulaire ne comprenant qu'une seule entrée de fichier. Si la requête POST réussit dans son intégralité, l'utilisateur se voit présenter un ensemble de formulaires pour chaque représentation de ressource image créée qui lui permet de spécifier plus de détails sur chacun (nom, description, etc.).

En cas d'échec de la création d'une ou de plusieurs ressources, le gestionnaire POST abandonne tout traitement et ajoute chaque message d'erreur individuel à un tableau. Ensuite, un conflit 419 est renvoyé et l'utilisateur est acheminé vers une page d'erreur de conflit 419 qui présente le contenu du tableau d'erreur, ainsi qu'un moyen de revenir au formulaire qui a été soumis.

Eric Fuller
la source
-2

Vous ne voudrez pas envoyer les en-têtes HTTP pour 100 lignes de commande. Vous ne souhaitez pas non plus générer plus de demandes que nécessaire.

Envoyez toute la commande dans un objet JSON au serveur, à: serveur / commande ou serveur / commande / nouveau. Renvoie quelque chose qui pointe vers: server / order / order_id

Pensez également à utiliser CREATE PUT au lieu de POST

Gai
la source
Je suppose qu'il a mentionné la méthode HTTP POST. La méthode CREATE HTTP n'existe pas.
Milan Novota le
N'est-ce pas? Oh attendez, il n'y en avait pas. Il y a eu PUT à la place.
Joyeux
22
Pourquoi diable utiliseriez-vous PUT pour créer du contenu? C'est exactement à quoi sert la méthode HTTP POST.
thecoshman
8
Vous utilisez PUT pour créer des ressources lorsque vous souhaitez que le client spécifie l'URI de la ressource, comme dans webdav. Je ne suis pas d'accord avec l'utilisation de PUT par l'affiche, mais il a sa place dans la création de ressources, bien que cet endroit puisse être limité dans sa portée.
user602525
2
Remarque: le POST d'une entité doit aboutir à ce que l'entité devienne un subordonné de la ressource adressée dans la demande et n'est pas idempotente. PUT remplace l'entité à l'adresse et est idempotent. L'idempotence (mot?) Est une attente importante des consommateurs.
Luke Puplett