Je construis une API RESTful qui prend en charge la mise en file d'attente de tâches longues pour une gestion éventuelle.
Le flux de travail typique de cette API serait:
- L'utilisateur remplit le formulaire
- Le client publie des données sur l'API
- L'API retourne 202 Accepté
- Le client redirige l'utilisateur vers une URL unique pour cette requête (
/results/{request_id}
) - ~ éventuellement ~
- Le client visite à nouveau l'URL et voit les résultats sur cette page.
Mon problème est à l'étape 6. Chaque fois qu'un utilisateur visite la page, j'envoie une demande à mon API ( GET /api/results/{request_id}
). Idéalement, la tâche sera terminée à présent et je vous renverrais 200 OK avec les résultats de leur tâche.
Mais les utilisateurs sont envahissants et j'attends de nombreux rafraîchissements trop zélés lorsque le résultat n'est pas encore terminé.
Quelle est ma meilleure option pour un code de statut pour indiquer que:
- cette demande existe,
- ce n'est pas encore fini,
- mais cela n'a pas non plus échoué.
Je ne m'attends pas à ce qu'un seul code communique tout cela, mais j'aimerais quelque chose qui me permette de transmettre des métadonnées au lieu que le client attende un contenu.
Il pourrait être logique de renvoyer un 202 car cela n’aurait aucune autre signification ici: c’est une GET
demande, donc rien n’est peut-être «accepté». Serait-ce un choix raisonnable?
L’alternative évidente à tout cela - qui fonctionne, mais fait échec à un des codes d’état - serait d’inclure toujours les métadonnées:
200 OK
{
status: "complete",
data: {
foo: "123"
}
}
...ou...
200 OK
{
status: "pending"
}
Ensuite , côté client, je (soupir) switch
sur response.data.status
pour déterminer si la demande a été complétée.
Est-ce ce que je devrais faire? Ou existe-t-il une meilleure alternative? Cela me semble tellement Web 1.0.
POST
demande ouverte. Le principal problème avec les longues interrogations ou les sockets Web est que l'utilisateur peut fermer le navigateur et revenir. Je pourrais les ouvrir à nouveau à ce moment-là (et c'est ce que je fais), mais il semble plus simple de disposer d'une seule API à appeler avant d'ouvrir ces sockets, car c'est un cas extrême pour lequel ce problème se pose.Réponses:
HTTP 202 accepté (HTTP / 1.1)
Vous recherchez un
HTTP 202 Accepted
statut. Voir RFC 2616 :Traitement HTTP 102 (WebDAV)
La RFC 2518 suggère d'utiliser
HTTP 102 Processing
:mais il y a une mise en garde:
Je ne sais pas comment interpréter la dernière phrase. Le serveur doit-il éviter d’envoyer quoi que ce soit pendant le traitement et ne répondre qu’après l’achèvement? Ou cela oblige-t-il uniquement à mettre fin à la réponse lorsque le traitement est terminé? Cela peut être utile si vous souhaitez signaler les progrès. Envoyez HTTP 102 et purgez la réponse octet par octet (ou ligne par ligne).
Par exemple, pour un processus long mais linéaire, vous pouvez envoyer cent points, après chaque caractère. Si le côté client (tel qu'une application JavaScript) sait qu'il doit s'attendre à exactement 100 caractères, il peut le faire correspondre à une barre de progression à afficher à l'utilisateur.
Un autre exemple concerne un processus qui comprend plusieurs étapes non linéaires. Après chaque étape, vous pouvez vider un message de journal qui sera éventuellement affiché à l'utilisateur, afin que l'utilisateur final puisse savoir comment le processus se déroule.
Problèmes avec le rinçage progressif
Notez que si cette technique a ses avantages, je ne la recommanderais pas . L'une des raisons est que cela force la connexion à rester ouverte, ce qui pourrait nuire à la disponibilité du service et ne pas être à la hauteur.
Une meilleure approche consiste à répondre avec
HTTP 202 Accepted
, soit à laisser l'utilisateur de vous contacter ultérieurement pour déterminer si le traitement est terminé (par exemple, en appelant de manière répétée un URI donné tel que celui/process/result
qui répondrait avec HTTP 404 non trouvé ou HTTP 409 en conflit jusqu'au traitement se termine et que le résultat est prêt), ou informe l'utilisateur lorsque le traitement est terminé si vous êtes en mesure de rappeler le client, par exemple, via un service de file d'attente de messages ( exemple ) ou WebSockets.Exemple pratique
Imaginez un service Web qui convertit des vidéos. Le point d'entrée est:
qui prend un fichier vidéo de la requête HTTP et fait un peu de magie avec elle. Imaginons que la magie consomme beaucoup de ressources processeur et que cela ne puisse se faire en temps réel pendant le transfert de la requête. Cela signifie qu'une fois le fichier transféré, le serveur répondra par un
HTTP 202 Accepted
contenu JSON, ce qui signifie «Oui, j'ai votre vidéo et je travaille dessus; il sera prêt quelque part dans le futur et sera disponible via l'ID 123. »Le client a la possibilité de s'abonner à une file de messages pour être averti à la fin du traitement. Une fois l’opération terminée, le client peut télécharger la vidéo traitée en allant sur:
qui mène à un
HTTP 200
.Que se passe-t-il si le client interroge cet URI avant de recevoir la notification? Eh bien, le serveur répondra avec,
HTTP 404
puisque la vidéo n’existe pas encore. Il peut être actuellement préparé. Cela n'a peut-être jamais été demandé. Il peut exister quelque temps dans le passé et être supprimé plus tard. Tout ce qui compte, c'est que la vidéo obtenue ne soit pas disponible.Maintenant, que se passe-t-il si le client se soucie non seulement de la vidéo finale, mais également de la progression (ce qui serait encore plus important s'il n'y avait pas de service de file d'attente de messages ou un mécanisme similaire)?
Dans ce cas, vous pouvez utiliser un autre noeud final:
ce qui entraînerait une réponse semblable à ceci:
Si vous faites la demande encore et encore, vous verrez les progrès jusqu'à ce que:
Il est essentiel de faire la différence entre ces trois types de demandes:
POST /video/convert
met en file d'attente une tâche. Il ne devrait être appelé qu'une seule fois: un nouvel appel mettrait en attente une tâche supplémentaire.GET /video/download/123
concerne le résultat de l'opération: la ressource est la vidéo. Le traitement — c'est ce qui s'est passé sous le capot pour préparer le résultat réel avant la demande et indépendamment pour la demande — est sans importance ici. Il peut être appelé une ou plusieurs fois.GET /video/status/123
concerne le traitement en soi . Il ne fait rien faire la queue. Il se fiche de la vidéo résultante. La ressource est le traitement lui-même. Il peut être appelé une ou plusieurs fois.la source
GET
, cependant? C'est certainement le bon choix pour l'initialePOST
, c'est pourquoi je l'utilise. Mais il semble sémantiquement suspectGET
de dire "accepté" alors qu’il n’accepte rien de cette demande particulière.POST
mis le travail en file d'attente, puis j'ai affichéGET
les résultats, éventuellement après la fermeture de la session par le client. Une 404 est aussi quelque chose que j'ai envisagé, mais cela semble faux, puisque la demande a été trouvée, elle n'a tout simplement pas été complétée. Cela m'indiquerait que le travail en file d'attente n'a pas été trouvé, ce qui est une situation très différente.GET
pièce, ne la considérez pas comme une requête incomplète , mais comme une requête visant à obtenir le résultat de l'opération . Par exemple, si je vous dis de convertir une vidéo et que cela vous prend cinq minutes, demander une vidéo convertie deux minutes plus tard devrait aboutir à HTTP 404, car la vidéo n'y est tout simplement pas encore présente. En revanche, la demande de progression de l'opération elle-même aboutira probablement à un HTTP 200 contenant le nombre d'octets convertis, la vitesse, etc.C'est la bonne façon de faire. L'état dans lequel se trouve une ressource par rapport à un journal spécifique à un domaine (ou logique métier) dépend du type de contenu de la représentation de la ressource.
Il y a deux concepts de différence regroupés ici qui sont en réalité différents. Le premier est le statut du transfert d'état entre le client et le serveur d'une ressource, et le second est l'état de la ressource elle-même, quel que soit le contexte dans lequel le domaine métier comprend les différents états de cette ressource. Ce dernier n’a rien à voir avec les codes d’état HTTP.
N'oubliez pas que les codes d'état HTTP correspondent au transfert d'état entre le client et le serveur de la ressource traitée, indépendamment des détails de cette ressource. Lorsque vous,
GET
une ressource, votre client demande au serveur la représentation d’une ressource dans son état actuel. Il peut s'agir d’une image d’un oiseau, d’un document Word ou d’un document externe. Le protocole HTTP s'en moque. Le code d'état HTTP correspond au résultat de cette demande. DuPOST
client au serveur, le serveur at-il transféré une ressource au serveur, où le serveur lui a ensuite donné une URL que le client peut afficher? Oui? Alors c'est une201 Created
réponse.La ressource pourrait être une réservation aérienne qui est actuellement dans l'état "à vérifier". Il peut également s'agir d'un bon de commande de produit dans l'état "approuvé". Ces états sont spécifiques à un domaine et ne concernent pas le protocole HTTP. Le protocole HTTP traite du transfert de ressources entre le client et le serveur.
Le point essentiel de REST et HTTP est que les protocoles ne se préoccupent pas des détails des ressources. Ceci est fait exprès, cela ne concerne pas les problèmes spécifiques à un domaine, de sorte qu'il puisse être utilisé sans avoir à rien savoir des problèmes spécifiques à un domaine. Vous ne réinterprétez pas la signification des codes de statut HTTP dans chaque contexte (système de réservation de transport aérien, système de traitement Imagine, système de sécurité vidéo, etc.).
Le domaine spécifique est que le client et le serveur déterminent entre eux en fonction
Content Type
de la ressource. Le protocole HTTP est agnostique à cela.En ce qui concerne la façon dont le client détermine que la ressource de demande a changé d'état, l'interrogation est votre meilleur choix, car elle garde le contrôle sur le client et ne suppose pas une connexion ininterrompue. Surtout si cela risque de prendre des heures jusqu'à ce que l'état change. Même si vous disiez au diable REST, vous allez simplement garder la connexion ouverte, le laisser ouvert pendant des heures et présumer que rien ne se passera mal serait une mauvaise idée. Que se passe-t-il si l'utilisateur ferme le client ou si le réseau s'éteint? Si la granularité est exprimée en heures, le client peut simplement demander l'état toutes les quelques minutes jusqu'à ce que la requête passe de 'en attente' à 'terminée'.
J'espère que ça aide à clarifier les choses
la source
J'ai trouvé les suggestions de ce blog raisonnables: tâches de repos et de longue durée .
Résumer:
la source
Le code d'état HTTP pour la ressource non encore disponible suggère de renvoyer une réponse de conflit 409, plutôt qu'une réponse 404, dans le cas où une ressource n'existe pas car elle est en cours de génération.
De la spécification w3 :
Cela est légèrement gênant, car le code 409 n’est "autorisé que dans les cas où il est prévu que l’utilisateur pourra peut-être résoudre le conflit et soumettre à nouveau la demande". Je suggère que le corps de la réponse contienne un message (éventuellement dans un format de réponse correspondant au reste de votre API), par exemple, "Cette ressource est en cours de génération. Elle a été lancée à [TIME] et devrait se terminer à [TIME]. S'il vous plaît Réessayez plus tard."
Notez que je ne suggérerais l'approche 409 que s'il est hautement probable que l'utilisateur qui demande la ressource est également l'utilisateur qui a initié la génération de cette ressource. Les utilisateurs ne participant pas à la génération de la ressource trouveraient une erreur 404 moins source de confusion.
la source