Quels modèles de conception éprouvés existent pour les opérations par lots sur les ressources au sein d'un service Web de style REST?
J'essaie de trouver un équilibre entre les idéaux et la réalité en termes de performance et de stabilité. Nous avons actuellement une API où toutes les opérations sont récupérées à partir d'une ressource de liste (par exemple: GET / user) ou sur une seule instance (PUT / user / 1, DELETE / user / 22, etc.).
Dans certains cas, vous souhaitez mettre à jour un seul champ d'un ensemble complet d'objets. Il semble très inutile d'envoyer la représentation complète de chaque objet dans les deux sens pour mettre à jour le champ.
Dans une API de style RPC, vous pourriez avoir une méthode:
/mail.do?method=markAsRead&messageIds=1,2,3,4... etc.
Quel est l'équivalent REST ici? Ou est-il acceptable de faire des compromis de temps en temps. Cela ruine-t-il la conception d'ajouter quelques opérations spécifiques où cela améliore vraiment les performances, etc.? Le client dans tous les cas est actuellement un navigateur Web (application javascript côté client).
la source
PATCH
- pas besoin de créativité dans ce cas.Pas du tout - je pense que l'équivalent REST est (ou au moins une solution est) presque exactement cela - une interface spécialisée conçue pour s'adapter à une opération requise par le client.
Je me souviens d'un modèle mentionné dans le livre de Crane and Pascarello Ajax in Action (un excellent livre, au fait - hautement recommandé) dans lequel ils illustrent la mise en œuvre d'une sorte d'objet CommandQueue dont le travail consiste à mettre les requêtes en file d'attente en lots et puis publiez-les périodiquement sur le serveur.
L'objet, si je me souviens bien, contenait essentiellement un tableau de "commandes" - par exemple, pour étendre votre exemple, chacun un enregistrement contenant une commande "markAsRead", un "messageId" et peut-être une référence à un callback / handler fonction - et ensuite selon un calendrier, ou sur une action de l'utilisateur, l'objet de commande serait sérialisé et posté sur le serveur, et le client gérerait le post-traitement conséquent.
Je n'ai pas les détails à portée de main, mais il semble qu'une file d'attente de commandes de ce type serait un moyen de gérer votre problème; cela réduirait considérablement le bavardage global et résumerait l'interface côté serveur d'une manière que vous pourriez trouver plus flexible sur la route.
Mise à jour : Aha! J'ai trouvé un extrait de ce livre en ligne, avec des exemples de code (bien que je suggère toujours de récupérer le livre!). Jetez un œil ici , en commençant par la section 5.5.3:
Voici deux fonctions pertinentes - une responsable de l'ajout de commandes à la file d'attente (
addCommand
), et une responsable de la sérialisation puis de leur envoi au serveur (fireRequest
):Cela devrait vous faire avancer. Bonne chance!
la source
Bien que je pense que @Alex est sur la bonne voie, conceptuellement, je pense que cela devrait être l'inverse de ce qui est suggéré.
L'URL est en effet "les ressources que nous ciblons" d'où:
signifie obtenir l'enregistrement du courrier avec l'ID 1 et
signifie patcher l'enregistrement de courrier avec l'ID 1. La chaîne de requête est un "filtre", filtrant les données renvoyées par l'URL.
Nous demandons donc ici tous les e-mails déjà marqués comme lus. Donc, pour [PATCH] sur ce chemin, il faudrait dire "patcher les enregistrements déjà marqués comme vrais" ... ce qui n'est pas ce que nous essayons de réaliser.
Donc, une méthode par lots, suivant cette réflexion devrait être:
bien sûr, je ne dis pas que c'est vrai REST (qui ne permet pas la manipulation d'enregistrements par lots), mais il suit plutôt la logique déjà existante et utilisée par REST.
la source
[GET]
format à faire[PATCH] mail?markAsRead=true data: [{"id": 1}, {"id": 2}, {"id": 3}]
(ou même justedata: {"ids": [1,2,3]}
)? Un autre avantage de cette approche alternative est que vous ne rencontrerez pas d'erreurs «414 Request URI too long» si vous mettez à jour des centaines / milliers de ressources dans la collection.Votre langage, "Cela me semble très inutile ...", indique pour moi une tentative d'optimisation prématurée. À moins qu'il ne puisse être démontré que l'envoi de la représentation entière des objets est un impact majeur sur les performances (nous parlons d'inacceptable pour les utilisateurs comme> 150 ms), il est inutile d'essayer de créer un nouveau comportement d'API non standard. N'oubliez pas que plus l'API est simple, plus elle est facile à utiliser.
Pour les suppressions, envoyez ce qui suit car le serveur n'a pas besoin de savoir quoi que ce soit sur l'état de l'objet avant la suppression.
L'idée suivante est que si une application rencontre des problèmes de performances concernant la mise à jour en masse des objets, il faut envisager de diviser chaque objet en plusieurs objets. De cette façon, la charge utile JSON est une fraction de la taille.
À titre d'exemple, lors de l'envoi d'une réponse pour mettre à jour les statuts «lu» et «archivé» de deux e-mails distincts, vous devrez envoyer ce qui suit:
Je diviserais les composants modifiables de l'e-mail (lu, archivé, importance, étiquettes) en un objet séparé car les autres (vers, depuis, sujet, texte) ne seraient jamais mis à jour.
Une autre approche à adopter consiste à tirer parti de l'utilisation d'un PATCH. Pour indiquer explicitement les propriétés que vous envisagez de mettre à jour et que toutes les autres doivent être ignorées.
Les gens déclarent que PATCH doit être implémenté en fournissant un tableau de modifications contenant: action (CRUD), chemin (URL) et changement de valeur. Cela peut être considéré comme une implémentation standard, mais si vous regardez l'intégralité d'une API REST, il s'agit d'une mise en œuvre ponctuelle non intuitive. En outre, la mise en œuvre ci-dessus est de savoir comment GitHub a implémenté PATCH .
Pour résumer, il est possible d'adhérer aux principes RESTful avec des actions par lots tout en conservant des performances acceptables.
la source
L'API google drive dispose d'un système vraiment intéressant pour résoudre ce problème ( voir ici ).
En gros, ils regroupent différentes demandes en une seule
Content-Type: multipart/mixed
demande, chaque demande complète individuelle étant séparée par un délimiteur défini. Les en-têtes et le paramètre de requête de la demande par lots sont hérités des demandes individuelles (c'est-à-direAuthorization: Bearer some_token
) à moins qu'ils ne soient remplacés dans la demande individuelle.Exemple : (tiré de leurs documents )
Demande:
Réponse:
la source
Je serais tenté dans une opération comme celle de votre exemple d'écrire un analyseur de plage.
Ce n'est pas très compliqué de créer un analyseur capable de lire "messageIds = 1-3,7-9,11,12-15". Cela augmenterait certainement l'efficacité des opérations générales couvrant tous les messages et est plus évolutif.
la source
Très bonne publication. Je cherche une solution depuis quelques jours. J'ai proposé une solution consistant à passer une chaîne de requête avec un ensemble d'identifiants séparés par des virgules, comme:
... puis en passant cela à une
WHERE IN
clause dans mon SQL. Cela fonctionne très bien, mais demandez-vous ce que les autres pensent de cette approche.la source
DELETE /books/delete?id=1,2,3
moment où le livre n ° 3 n'existe pas -WHERE IN
il ignorera silencieusement les enregistrements, alors que je m'attendrais généralementDELETE /books/delete?id=3
à 404 si 3 n'existe pas.De mon point de vue, je pense que Facebook a la meilleure implémentation.
Une seule requête HTTP est effectuée avec un paramètre de lot et une pour un jeton.
Dans le lot, un json est envoyé. qui contient une collection de "demandes". Chaque requête a une propriété de méthode (get / post / put / delete / etc ...), et une propriété relative_url (uri du point de terminaison), en plus les méthodes post et put permettent une propriété "body" où les champs à mettre à jour sont envoyés .
plus d'informations sur: Facebook batch API
la source