Représenter les actions (verbes) dans l'URI REST

16

J'ai une opération d'impression à effectuer pour mes documents clients. J'ai également besoin des autres opérations standard, comme ajouter, mettre à jour, supprimer. donc j'ai:

  • Pour créer un nouveau client:
    URI = / customer / {id}, type = POST, Methodname = CreateCustomer ()
  • Pour la mise à jour:
    URI: / customer / {id}, type = PUT, method = UpdateCstomer ()
  • Pour Delete customer:
    URI = / customer / {id}, type = DELETE, Methodname = DeleteCustomer ()
  • Pour View:
    URI: / customer / {id}, tapez = GET, method = GetCustomer ()

Maintenant, si j'ai besoin d'imprimer un document pour ce client, j'ai besoin d'une fonction d'impression. Mon URI peut ressembler à ceci: / customer / {id}, type = POST, method = PrintCustomer (). Mais j'ai utilisé ce type URI et POST pour CreateCustomer. Je voulais que l'URI ressemble à ceci: / customer / Print / {id}, type = POST, method = PrintCustomer ().

Mais je ne peux pas avoir le verbe "Imprimer" dans mon URI. Quelle est la meilleure façon de procéder? J'ai pensé à / client / document / {id} comme l'URI ... mais je vais rencontrer le même problème. J'aurais les opérations CRUD sur le "document". Donc, encore une fois, je suis à court de ce que j'utiliserais pour "imprimer". S'il vous plaît donnez votre avis.

Nitya Maha
la source
2
L'impression est généralement une opération côté client, donc je suis curieux - comment votre configuration est-elle telle qu'elle vous oblige à envoyer une commande à un serveur REST?
Shauna
2
@Shauna Pas nécessairement, l'URI peut être une demande au serveur pour une version imprimable de la ressource (c'est-à-dire une vue différente).
Evan Plaice
1
@EvanPlaice - Assez bien, même si cela laisse le fait d'imprimer au client (qui, même après avoir récupéré une version imprimable côté serveur, déciderait alors sur quel périphérique imprimer et enverrait la commande d'impression elle-même, même si cela va à un serveur d'impression). Une demande pour obtenir une version imprimable d'une ressource serait alors logiquement ... eh bien ... GET.
Shauna
@Shauna Déclencher un travail d'impression à partir d'une seule requête HTTP serait impossible en raison de la sécurité du navigateur. Une demande de version imprimable n'est qu'une demande GET mais vous avez toujours besoin d'un moyen de spécifier que le navigateur doit rendre la version imprimable. Vous pouvez spécifier une URL différente, mais cela violerait les principes de REST car vous ne demandez pas réellement une ressource différente, juste une transformation différente de la même ressource. D'où la raison de spécifier la transformation via un paramètre de requête et / ou un type de contenu.
Evan Plaice
Je n'ai pas assez de représentants pour poster une réponse, mais je trouve intéressant que tyk.io/rest-never-crud soutienne que POST /customers/123/printc'est une chose valable à faire.
jlh

Réponses:

9

POSTne signifie pas "créer", cela signifie "processus". Vous pouvez créer une nouvelle ressource en publiant une demande appropriée sur une ressource existante (c'est-à-dire publier /customerspour créer un nouveau client). Mais vous pouvez également utiliser POSTpour remplir toutes les autres actions qui ne correspondent pas à un paradigme CRUD soigné.

Dans le cas de l'impression, vous devez considérer l'acte d'imprimer comme une ressource elle-même. Vous demandez au système de créer un "travail d'impression" pour vous. Cela signifie que vous pouvez avoir une prints/ressource qui sert de conteneur pour toutes les impressions demandées. Lorsque vous souhaitez imprimer quelque chose, vous POSTun document à cette ressource qui contient toutes les informations sur l'impression que vous souhaitez créer, en identifiant les ressources que vous souhaitez imprimer avec des liens vers elles.

En tant que document JSON, il pourrait ressembler à ceci:

{
   contents: ["http://site/customers/12345"],
   paper-size: "A4",
   duplex: "true"
}

De toute évidence, vous devez personnaliser cela pour qu'il corresponde à ce que vous voulez faire. L'essentiel est que vous identifiez d'autres ressources à imprimer en spécifiant leur URL.

En réponse à la demande, vous pouvez simplement retourner un 200 OKou un 204 No-Contentet le traiter comme un processus de tir et d'oubli. Cependant, si vous souhaitez l'améliorer, vous pouvez retourner 201 Createdet spécifier l'URL du travail d'impression nouvellement créé, par exemple /prints/12345.

Un utilisateur peut alors effectuer une GETsur la ressource pour voir l'état de son travail d'impression (en attente, en cours, etc.), ou peut demander l'annulation du travail en émettant un DELETE.

Une fois que vous reformulez le problème en termes de ressources, la conception RESTful devrait venir naturellement et vous donner la possibilité de vous étendre et de vous améliorer d'une manière que vous n'avez peut-être pas immédiatement envisagée.

Paul Turner
la source
2
POST signifie généralement créer / insérer, tandis que mettre signifie généralement mettre à jour enregistrer / mettre à jour. C'est ainsi qu'il est défini dans REST même si ce n'est pas la façon dont il est généralement utilisé en HTML.
Evan Plaice
2
@EvanPlaice la spécification HTTP nomme PUT comme verbe de création / mise à jour (elle utilise un modèle de création + mise à jour au lieu du modèle plus familier de création + récupération + mise à jour) et POST est le verbe "traitement des données", ainsi que le verbe "ajouter" . Roy Fielding dans son blog a décrit POST comme le verbe à utiliser lorsque vous ne voulez pas standardiser l'opération. POST prend la sémantique «créer» lorsque vous envisagez d'ajouter un nouvel élément à une collection d'éléments. Dans ce cas, Tragedian a frappé le clou sur la tête en utilisant POST pour traiter ou ajouter un travail d'impression.
Rob
@RobY OK, c'est logique. Par exemple, PUT pourrait être utilisé pour représenter un SPROC conçu pour entrer des données dans une base de données. Attendu qu'un POST pourrait constituer les étapes intermédiaires et les mutations nécessaires pour collecter / préparer ces données. La conception de l'opération POST pourrait changer ou être remplacée à mesure que la conception évolue, mais les opérations PUT représentent le modèle qui (idéalement) ne devrait pas changer. Je mettrais à jour ma réponse, mais celle-ci explique déjà très bien la différence.
Evan Plaice
4

J'ai fait ça avant. Pour imprimer un document, je renvoie simplement une version pdf d'une ressource. Le client doit seulement envoyer une demande GET pour la ressource avec Accept header application / pdf.

Cela évite également de créer un nouvel URI pour une ressource temporaire comme un travail d'impression. L'utilisation de l'en-tête HTTP fait également partie de REST et garde l'URI propre.

imel96
la source
3

Ajoutez simplement un paramètre au GET de l'URI actuel

Il est assez typique d'utiliser un URI pour plusieurs actions.

Si vous parlez de la même ressource mais d'une action différente, vous la définiriez comme paramètre.

/ client / {id}? print = true

Ensuite, lorsque vous définissez votre méthode GET, vous détectez la présence du paramètre d'impression et le gérez différemment.

REST est défini de la manière suivante:

  • POST - Créer un enregistrement, un actif ou une ressource
  • PUT - Mise à jour, un enregistrement, un actif ou une ressource
  • SUPPRIMER - Supprimer un, un enregistrement, un actif ou une ressource

GET, d'autre part, est destiné à être utilisé de plusieurs manières car il existe généralement de nombreuses formes différentes qu'une ressource peut être récupérée. C'est également pourquoi les requêtes GET sont représentées sous la forme d'une chaîne de requête. Si vous travailliez avec une ressource de base de données, vous récupéreriez littéralement une vue via une requête, mais REST est intentionnellement abstrait à un niveau supérieur car il est conçu pour gérer de nombreux types de ressources différents.

La spécification REST est assez avant-gardiste, même si les API ne commencent à l'utiliser que récemment.

Si vous souhaitez en savoir plus sur les protocoles REST, je vous suggère fortement de lire " Haters Gonna Hate HATEOAS ".


Mise à jour:

@Shauna a souligné un trou intéressant dans mon raisonnement. Il n'y a pas de vraie bonne façon et de nombreuses formes sont considérées comme acceptables. J'y ai réfléchi un peu plus et puisque votre utilisation prévue est de transformer les données en une représentation différente, il est logique de définir la transformation comme un nouveau type MIME.

Par exemple, vous pouvez représenter l'URI comme:

/customer/{id}+print

Où vous pouvez définir la réponse Content-Type sur text / html + print. De cette façon, vous auriez également la possibilité de définir plus de transformations à l'avenir.

Par exemple:

// for application/json
/customer/{id}+json

// for application/atom+xml
/customer/{id}+atom

Dans tous les cas, tous les formulaires sont acceptables. L'implémentation que vous décidez dépend davantage des préférences personnelles et des capacités de votre serveur.

À part: Permettez-moi de clarifier car il semble y avoir une certaine confusion. Le paramètre de requête et / ou le type de contenu 'print' est utilisé pour spécifier comment la ressource est transformée. Pas comment déclencher un travail d'impression physique. Pour des raisons de sécurité, l'accès au niveau matériel est toujours laissé à l'utilisateur / client / navigateur.

Plie d'Evan
la source
Pour ajouter - Au lieu d'utiliser la chaîne de requête ( ?print=true), vous pouvez également utiliser les paramètres URI (c'est-à-dire - /customer/{id}/printable). Lequel vous utiliserez dépendra en grande partie de la norme que votre système (CMS, framework, code en général) est configuré pour gérer. Les deux sont considérés comme valides et acceptables .
Shauna
@Shauna Techniquement, la meilleure approche serait d'employer un type MIME spécifique à l'impression avec l'URI '/ customer / {id} + print' et une réponse de type MIME text / html + print. L'avantage d'une telle approche étant que vous pouvez créer des transformations pour de nombreux types MIME (ex text / html, text / x-markdown, application / json, etc.) pour le même URI. L'inconvénient de la solution que vous présentez est que vous devez créer un URI supplémentaire (et définir une autre route) pour chaque type MIME différent. Cela va un peu à l'encontre du but de l'utilisation de REST.
Evan Plaice
(suite) Je dirais que les hacks URI sont un anti-modèle introduit principalement par la communauté ROR mais cela ne signifie pas qu'ils ne sont pas utiles. Avec l'arrivée de meilleurs serveurs HTTPd de bas niveau, il devient de plus en plus facile d'implémenter REST d'une manière qui exploite pleinement son potentiel. Les choses ont parcouru un long chemin depuis l'époque où Apache et tout acheminer via index.html était la seule option.
Evan Plaice
2
GET ne devrait pas faire de changements d'état ni avoir d'effets secondaires. Considérez que GET est idempotent, ce qui signifie que le middleware peut réessayer la demande s'il ne l'a pas vu passer. Dans ce cas, chaque nouvelle tentative entraînerait une nouvelle copie fraîchement imprimée du document. ;)
Rob
@RobY Je supposais que l'action «imprimer» n'allait pas gérer le processus d'impression physique du document car cela serait mieux servi par le navigateur et le pilote d'impression. Au contraire, la sortie média / impression renverrait une représentation «imprimable» du document. Par conséquent, l'idempotence est maintenue. Bon point, envoyer des travaux d'impression sur Internet de manière apatride serait un mauvais moment.
Evan Plaice