REST API-Bulk créer ou mettre à jour en une seule requête
supposons qu'il y ait deux ressources Binder
et Doc
avec une relation d'association qui signifie que les Doc
et Binder
sont autonomes. Doc
pourrait appartenir ou non à Binder
et Binder
pourrait être vide.
si je veux concevoir une API REST qui permet à l'utilisateur d'envoyer une collection de Doc
s, dans une seule requête , comme suit:
{
"docs": [
{"doc_number": 1, "binder": 1},
{"doc_number": 5, "binder": 8},
{"doc_number": 6, "binder": 3}
]
}
et pour chaque doc dans le docs
,
- si le
doc
existe, alors assignez-le àBinder
- Si le
doc
n'existe pas, le créer et attribuer
je suis vraiment confus quant à la façon dont cela devrait être mis en œuvre.
- Quelle méthode HTTP utiliser?
- quel code de réponse doit être retourné?
- est-ce que c'est admissible pour le repos?
- à quoi ressemblerait L'URI?
/binders/docs
? - traitement de la demande globale, que se passe-t-il si quelques éléments soulèvent une erreur, mais l'autre passer. Quel code de réponse doit être retourné? Si l'opération en bloc atomique?
4 réponses
je pense que vous pourriez utiliser une méthode de poteau ou de PATCH pour gérer cela car ils conçoivent généralement pour cela.
-
L'utilisation d'une
POST
méthode est typiquement utilisé pour ajouter un élément lorsqu'il est utilisé sur la ressource de liste, mais vous pouvez également soutenir plusieurs actions pour cette méthode. Voir la réponse: . Vous pouvez également prendre en charge différents formats de représentation pour l'entrée (si elles correspondent à un tableau ou d'un seul des éléments).dans le cas, il n'est pas nécessaire de définir votre format pour décrire la mise à jour.
-
utilisant une "méthode 151930920" est également approprié car les requêtes correspondantes correspondent à une mise à jour partielle. Selon RFC5789 ( http://tools.ietf.org/html/rfc5789 ):
Plusieurs applications étendant le protocole de transfert hypertexte (HTTP) nécessitent une fonctionnalité pour effectuer une modification partielle de la ressource. La méthode HTTP PUT existante ne permet qu'un remplacement complet d'un document. Cette proposition ajoute une nouvelle méthode HTTP, PATCH, pour modifier une ressource HTTP existante.
Dans le cas, vous devez définir votre format pour décrire la mise à jour partielle.
je pense que dans ce cas, POST
et PATCH
sont assez similaires car vous n'avez pas vraiment besoin de décrire l'opération à faire pour chaque élément. Je dirais que ça dépend du format de la représentation à envoyer.
le cas de PUT
est un peu moins clair. En fait , lorsque vous utilisez une méthode PUT
, vous devez fournir la liste complète. En fait, la représentation fournie dans la demande remplacera la personne-ressource de la liste.
Vous pouvez avoir deux options concernant les chemins de ressources.
- utilisant le chemin de la ressource pour la liste doc
Dans ce cas, vous devez explicitement fournir le lien de docs avec un liant dans la représentation que vous fournissez dans la demande.
Voici un exemple d'itinéraire pour ce /docs
.
Le contenu d'une telle approche pourrait être pour la méthode POST
:
[
{ "doc_number": 1, "binder": 4, (other fields in the case of creation) },
{ "doc_number": 2, "binder": 4, (other fields in the case of creation) },
{ "doc_number": 3, "binder": 5, (other fields in the case of creation) },
(...)
]
- utilisant le chemin des sous-Ressources de l'élément de reliure
en outre, vous pouvez également envisager de tirer parti des sous-routes pour décrire le lien entre les documents et les reliures. Les conseils concernant l'association entre un doc et un classeur n'ont pas à être spécifiés dans le contenu de la requête.
Voici un exemple d'itinéraire pour ce /binder/{binderId}/docs
. Dans ce cas, l'envoi d'une liste de documents avec une méthode POST
ou PATCH
attachera des docs au classeur avec l'identificateur binderId
après avoir créé le doc s'il n'existe pas.
Le contenu d'une telle approche pourrait être pour la méthode POST
:
[
{ "doc_number": 1, (other fields in the case of creation) },
{ "doc_number": 2, (other fields in the case of creation) },
{ "doc_number": 3, (other fields in the case of creation) },
(...)
]
Quant à la réponse, c'est à vous de définir le niveau de réponse et les erreurs de retour. Je vois deux niveaux: le niveau de statut (niveau global) et le niveau de charge utile (plus mince niveau.) C'est aussi à vous de définir si toutes les insertions, mises à jour correspondant à votre demande doit être atomique ou pas.
- Atomique
dans ce cas, vous pouvez utiliser le statut HTTP. Si tout va bien, vous obtenez un statut 200
. Si non, un autre statut comme 400
si les données fournies ne sont pas correctes (par exemple binder id non valide) ou autre chose.
- Non atomique
dans ce cas, un État 200
sera retourné et c'est à la représentation de réponse de décrire ce qui a été fait et où des erreurs se produisent éventuellement. ElasticSearch a un paramètre dans son API REST pour la mise à jour globale. Cela pourrait vous donner quelques idées à ce niveau: http://www.elasticsearch.org/guide/en/elasticsearch/guide/current/bulk.html .
- asynchrone
vous pouvez également mettre en œuvre un traitement asynchrone pour traiter les données fournies. Dans ce cas, les retours de statut HTTP seront 202
. Le client doit tirer une ressource supplémentaire pour voir ce qui se passe.
avant de terminer, je voudrais également noter que la spécification OData aborde la question des relations entre les entités avec la fonctionnalité nommé liens de navigation . Peut-être pourriez-vous jeter un oeil à ce ;-)
le lien suivant peut aussi vous aider: https://templth.wordpress.com/2014/12/15/designing-a-web-api / .
J'espère que ça vous aidera, Thierry
vous devrez probablement utiliser POST ou PATCH, car il est peu probable qu'une seule requête qui met à jour et crée plusieurs ressources soit idempotent.
Faire PATCH /docs
est certainement une option valable. Vous pourriez trouver difficile d'utiliser les formats de patch standard pour votre scénario particulier. Pas sûr à ce sujet.
vous pouvez utiliser 200. Vous pouvez également utiliser 207 - Multi Statut
This peut être fait dans un cadre Reposant. La clé, à mon avis, est d'avoir une ressource qui est conçu pour accepter un ensemble de documents à mettre à jour/créer.
si vous utilisez la méthode PATCH, je pense que votre opération devrait être atomique. c'est-à-dire que je n'utiliserais pas le code de statut 207 et ne signalerais pas les succès et les échecs dans l'organisme de réponse. Si vous utilisez L'opération de POST, alors l'approche 207 est viable. Vous devrez concevoir votre propre corps de réponse pour communiquer quelles opérations a réussi et qui a échoué. Je ne suis pas au courant normalisée.
METTRE ing
PUT /binders/{id}/docs
créer ou mettre à jour, et relier un document unique à un classeur
p.ex.:
PUT /binders/1/docs HTTP/1.1
{
"docNumber" : 1
}
PATCH ing
PATCH /docs
créer des documents s'ils n'existent pas et les relier à des liants
p.ex.:
PATCH /docs HTTP/1.1
[
{ "op" : "add", "path" : "/binder/1/docs", "value" : { "doc_number" : 1 } },
{ "op" : "add", "path" : "/binder/8/docs", "value" : { "doc_number" : 8 } },
{ "op" : "add", "path" : "/binder/3/docs", "value" : { "doc_number" : 6 } }
]
je vais inclure plus d'informations plus tard, mais en attendant si vous voulez, jeter un oeil à RFC 5789 , RFC 6902 et William Durand S'il vous plaît. Ne pas Patch comme un Idiot entrée de blog.
dans un projet où j'ai travaillé, nous avons résolu ce problème en implémentant quelque chose que nous avons appelé des requêtes "Batch". Nous avons défini un chemin /batch
où nous avons accepté json dans le format suivant:
[
{
path: '/docs',
method: 'post',
body: {
doc_number: 1,
binder: 1
}
},
{
path: '/docs',
method: 'post',
body: {
doc_number: 5,
binder: 8
}
},
{
path: '/docs',
method: 'post',
body: {
doc_number: 6,
binder: 3
}
},
]
la réponse a le code d'état 207 (Multi-statut) et ressemble à ceci:
[
{
path: '/docs',
method: 'post',
body: {
doc_number: 1,
binder: 1
}
status: 200
},
{
path: '/docs',
method: 'post',
body: {
error: {
msg: 'A document with doc_number 5 already exists'
...
}
},
status: 409
},
{
path: '/docs',
method: 'post',
body: {
doc_number: 6,
binder: 3
},
status: 200
},
]
vous pouvez également ajouter un support pour les en-têtes dans cette structure. Nous avons mis en œuvre quelque chose qui s'est avéré utile qui était des variables à utiliser entre les requêtes dans un lot, ce qui signifie que nous pouvons utiliser la réponse d'une demande d'entrée à l'autre.
Facebook et Google ont des implémentations similaires:
https://developers.google.com/gmail/api/guides/batch
https://developers.facebook.com/docs/graph-api/making-multiple-requests
quand vous voulez créer ou mettre à jour une ressource avec le même appel que j'utiliserais postez ou mettez selon le cas. Si le document existe déjà, voulez-vous de tout le document:
- remplacé par le document que vous envoyez (c.-à-d. les propriétés manquantes dans la demande seront supprimées et déjà existantes écrasées)?
- fusionné avec le document que vous envoyez (c.-à-d. les propriétés manquantes dans la requête ne seront pas supprimés et les propriétés déjà existantes seront écrasées)?
In si vous voulez le comportement de l'alternative 1, vous devez utiliser un POST et dans le cas où vous voulez le comportement de l'alternative 2, vous devez utiliser PUT.
http://restcookbook.com/HTTP%20Methods/put-vs-post /
comme les gens l'ont déjà suggéré, vous pouvez également opter pour PATCH, mais je préfère garder simple API et ne pas utiliser de verbes supplémentaires si elles ne sont pas nécessaires.