Comment concevoir une API REST pour gérer les opérations non CRUD?

11

J'essaie de convertir un ensemble de services basés sur SOAP en une API RESTful.

J'ai commencé par identifier les ressources en analysant les noms des opérations et j'ai obtenu la ressource Subscription.

Lorsque j'ai besoin de mettre à jour l'état de l'abonnement, je ne peux pas simplement envoyer une POSTdemande au serveur, car je n'ai pas d'accès direct aux ressources, mais je dois appeler des opérations de style RPC pour mettre à jour leurs propriétés. De plus, uniquement et uniquement si je modifie l'état de l'abonnement sur "actif", un appel supplémentaire vers un service externe est requis.

Dans ces cas, quelle est la meilleure pratique pour gérer les opérations sous-jacentes?

La solution que j'ai trouvée est d'utiliser des paramètres de requête, de sorte que si je dois appeler le service d'activation, je peux utiliser quelque chose comme:

POST /subscriptions/{subscriptionid}/?activate=true

Étant donné que je ne peux pas mettre à jour directement mes champs d'objet d'abonnement, existe-t-il une meilleure pratique pour gérer ce type de conversion?

Mise à jour 1:

Je peux mettre dans le corps de ma requête POST quelques valeurs, par exemple "état": "actif"

et vérifier à l'intérieur de mon service les opérations à déclencher.

Vektor88
la source
Le mappage de REST des commandes aux verbes HTTP échoue avec des opérations complexes. Vous feriez mieux de simplement faire un appel de style RPC POST activateSubscription / {id} personne ne sera confus par cela
Ewan
@Ewan Je ne suis pas sûr que cela soit conforme au modèle RESTful, mais j'ai trouvé une autre solution: dans mon code, je peux appeler la bonne opération de style RPC en fonction de la charge utile d'entrée (je peux passer state = active dans le corps de ma demande de publication, le code appellera le code d'activation)
Vektor88
1
Une mise à jour d'une ressource existante comme celle-ci devrait être un PATCH, et le corps de la requête est alors un modèle partiel de ce que vous modifiez. Un POST est censé être une requête qui crée une ressource. Cette distinction, en plus d'être plus claire pour l'utilisateur, rendra plus facile pour votre code de savoir quand cette opération se produit plutôt qu'une publication de ressource.
M. Cochese
1
@ Vektor88 Typiquement, mais ce sont des opérations idempotentes où vous devez passer la représentation complète de l'état des ressources. Ce cas d'utilisation ressemble beaucoup plus à une mise à jour partielle, qui correspond très bien à un PATCH.
M. Cochese
1
@MrCochese POST n'est pas idempotent.
JimmyJames

Réponses:

8

Vous devez regarder cette conférence de Jim Webber.

Lorsque j'ai besoin de mettre à jour l'état de l'abonnement, je ne peux pas simplement envoyer une demande POST au serveur, car je n'ai pas d'accès direct aux ressources, mais je dois appeler certaines opérations de style RPC pour mettre à jour leurs propriétés. De plus, uniquement et uniquement si je modifie l'état de l'abonnement sur "actif", un appel supplémentaire vers un service externe est requis.

Pensez "messagerie"; envoyer un message à votre domaine, décrivant ce que vous voulez que cela se produise. L'effet secondaire du message est que votre modèle de domaine change réellement son état. La "ressource" est la file d'attente de messages.

POST /subscriptions/{subscriptionid}/?activate=true

L'orthographe du nom de la ressource n'a pas d'importance pour les machines; mais les gens ont tendance à devenir pointilleux lorsque les identifiants que vous utilisez ne respectent pas la convention selon laquelle les ressources sont des "noms".

En outre, nous parlons d'une ressource qui est subordonnée à /subscriptions/{subscriptionid}, donc la convention (voir RFC 3986 ) appelle à exprimer cette relation avec un segment de chemin, plutôt que d'utiliser la partie requête.

Donc, ces orthographes pourraient être raisonnables

POST /subscriptions/{subscriptionid}/messages
POST /subscriptions/{subscriptionid}/activations
VoiceOfUnreason
la source
1
La conférence de Jim Webber est disponible sur youtube.com/watch?v=aQVSzMV8DWc
user674669
0

Si c'est un drapeau booléen pour activer / désactiver des trucs, je dirais que la valeur par défaut est d'utiliser JSON:

POST /subscriptions/{subscriptionid}/
{
    format: 0,
    subscription: 
    {
        active: false
    }
}

Ceci est facilement étendu si vous souhaitez prendre en charge plus de propriétés. Une autre approche lui donne son propre point final:

POST /subscriptions/{subscriptionid}/active/
DELETE /subscriptions/{subscriptionid}/active/

Personnellement, je n'utiliserais cela que si l' activeétat de cet événement a besoin / possède des propriétés que vous pouvez ensuite passer / obtenir en JSON, comme un ID utilisateur ou un paramètre.

Si ce n'est pas une valeur booléenne mais juste une action que vous devez déclencher mais dont vous n'avez pas besoin / n'avez aucun retour d'état (à l'exception d'un 200 OK immédiat), j'utiliserais un point de terminaison comme celui-ci pour le déclencher un peu comme un RPC:

POST /subscriptions/{subscriptionid}/activate/

En cas de doute, lisez ceci: http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api#restful (voir "Qu'en est-il des actions qui ne rentrent pas dans le monde des opérations CRUD? ")

Barry Staes
la source
0

REST n'est pas fonctionnel. Activateest un verbe et ne peut pas être un état, Activeest un état.

Étant donné que RESTful n'est pas fonctionnel, vous ne pouvez pas dire à un service RESTful quoi faire, mais vous pouvez ajouter du travail pour la file d'attente d'un service.

Regarde ça:

PUT /subscriptionQueue
subscriptionId={subscriptionId}
active=true

Cette demande est RESTful et prend en charge tous les avantages de RESTful (comme les performances, l'acide ...)

Peter Rader
la source