web-dev-qa-db-fra.com

REST - applications complexes

J'ai du mal à appliquer les principes RESTful à une nouvelle application Web sur laquelle je travaille. En particulier, l'idée est que pour être RESTful, chaque requête HTTP doit contenir suffisamment d'informations par elle-même pour que son destinataire puisse la traiter en parfaite harmonie avec la nature sans état de HTTP.

L'application permet aux utilisateurs de rechercher des médicaments. La recherche accepte les filtres en tant qu'entrée, par exemple, renvoyer les médicaments interrompus, inclure un traitement complémentaire, etc. Au total, environ 30 filtres peuvent être appliqués.

De plus, les détails du patient peuvent être entrés, y compris l'âge du patient, le sexe, les médicaments actuels, etc.

Pour être reposant, toutes ces informations doivent-elles être incluses dans chaque demande? Cela semble entraîner une surcharge considérable sur le réseau. En outre, les restrictions sur la longueur des URL, du moins pour GET, ne rendraient-elles pas cela impossible?

47
Alistair77

Le "filtre comme ressource" est un tact parfait pour cela.

Vous pouvez insérer la définition de filtre dans la ressource de filtre et renvoyer l'ID de filtre.

PUT est idempotent. Par conséquent, même si le filtre existe déjà, il vous suffit de détecter que vous avez déjà vu le filtre pour pouvoir renvoyer l'ID correct pour le filtre.

Ensuite, vous pouvez ajouter un paramètre de filtre à vos autres demandes, et elles peuvent récupérer le filtre à utiliser pour les requêtes.

GET/médicaments? Filter = 1234 & page = 4 & pagesize = 20

Je voudrais exécuter les filtres bruts à travers une sorte de processus de canonisation, juste pour avoir un ensemble normalisé, de sorte que, par exemple, Le filtre "prénom = Bob nom = Eubanks" est identique à "nom = prénom Eubanks = Bob". C'est juste moi si.

La seule préoccupation réelle est que, avec le temps, vous devrez peut-être rendre obsolètes certains filtres. Vous pouvez simplement renvoyer la demande par erreur si quelqu'un fait une demande avec un filtre manquant ou obsolète.

Modifier la réponse à la question ...

Commençons par les fondamentaux.

Simplement, vous voulez spécifier un filtre à utiliser dans les requêtes, mais ces filtres sont (potentiellement) impliqués et compliqués. Si c'était simple/médicaments/1234, ce ne serait pas un problème.

Effectivement, vous devez toujours envoyer le filtre à la requête. La question est de savoir comment représenter ce filtre.

Le problème fondamental avec des choses comme les sessions dans REST est qu’elles sont généralement gérées "hors bande". Quand vous dites, par exemple, allez créer un médicament, vous PUT ou POST à la ressource médicaments, et vous obtenez une référence à ce médicament.

Avec une session, vous récupérez (généralement) un cookie, ou peut-être un autre jeton représentant cette session. Si votre PUT à la ressource médicaments a également créé une session, votre requête a en réalité créé deux ressources: un médicament et une session.

Malheureusement, lorsque vous utilisez quelque chose comme un cookie et que vous avez besoin de ce cookie pour votre demande, le nom de la ressource n'est plus la représentation fidèle de la ressource. Maintenant, c'est le nom de la ressource (l'URL) et le cookie.

Donc, si je fais un GET sur la ressource nommée/médicaments/recherche et que le cookie représente une session, et que cette session contient un filtre, vous pouvez voir comment, en réalité, le nom de cette ressource,/médicaments/recherche, n'est pas vraiment utile du tout. Je ne dispose pas de toutes les informations nécessaires pour une utilisation efficace, en raison des effets indésirables du cookie, de la session et du filtre qui y est contenu.

Maintenant, vous pourriez peut-être réécrire le nom:/medications/search? Session = ABC123, en incorporant efficacement le cookie dans le nom de la ressource.

Mais maintenant, vous rencontrez le contrat typique des sessions, notamment leur courte durée. Cette ressource nommée est donc moins utile, à long terme, non inutile, mais moins utile. En ce moment, cette requête me donne des données intéressantes. Demain? Probablement pas. Je vais avoir une mauvaise erreur sur la fin de la session.

L'autre problème est que les sessions ne sont généralement pas gérées en tant que ressource. Par exemple, ils sont généralement un effet secondaire, vs explicitement gérés via GET/PUT/DELETE. Les sessions sont également le "tas de déchets" de l'état de l'application Web. Dans ce cas, nous espérons simplement que la session est correctement renseignée avec ce qui est nécessaire pour cette demande. En fait, nous ne savons pas vraiment. Encore une fois, c'est un effet secondaire.

Maintenant, retournons un peu la tête. Utilisons/médicaments/recherche? Filter = ABC123.

Évidemment, cela semble identique. Nous venons de changer le nom de "session" en "filtre". Mais, comme indiqué, les filtres, dans ce cas, SONT une "ressource de première classe". Ils doivent être créés, gérés, etc. de la même manière qu’un médicament, un fichier JPEG ou toute autre ressource de votre système. C'est la distinction clé.

Certes, vous pouvez traiter les "sessions" comme une ressource de premier ordre, les créer, y insérer directement des éléments, etc. Mais vous pouvez voir comment, du moins du point de vue de la clarté, une session de "premier cours" une bonne abstraction pour ce cas. Utiliser une session, c’est comme aller chez le nettoyeur et remettre tout votre sac à main ou votre porte-documents. "Oui, le billet est quelque part là-dedans, creuser ce que vous voulez, donnez-moi mes vêtements", surtout comparé à quelque chose d'explicite comme un filtre.

Ainsi, vous pouvez voir qu’à 30 000 pieds, il n’ya pas beaucoup de différence entre un filtre et une session. Mais lorsque vous effectuez un zoom avant, ils sont assez différents.

Avec la ressource de filtrage, vous pouvez choisir d’en faire une chose persistante pour toujours et à jamais. Vous pouvez les faire expirer, vous pouvez faire ce que vous voulez. Les sessions ont généralement une sémantique préconçue: courte durée, durée de la connexion, etc. Les filtres peuvent avoir la sémantique de votre choix. Ils sont complètement séparés de ce qui vient avec une session.

Si je le faisais, comment pourrais-je travailler avec des filtres?

Je suppose que le contenu d'un filtre ne m'intéresse pas vraiment. Plus précisément, je doute que je puisse interroger "tous les filtres qui effectuent une recherche par prénom". À ce stade, il semble que des informations inintéressantes, je ne vais pas concevoir autour de cela.

Ensuite, je normaliserais les filtres, comme je l’ai mentionné ci-dessus. Assurez-vous que les filtres équivalents sont vraiment équivalents. Vous pouvez le faire en triant les expressions, en vous assurant que les noms de champs sont tous en majuscules, ou quoi que ce soit.

Ensuite, je stockais le filtre en tant que document XML ou JSON, selon ce qui est le plus confortable/approprié pour l'application. Je donnerais à chaque filtre une clé unique (naturellement), mais je stockerais aussi un hachage pour le document réel avec le filtre.

Je le ferais pour pouvoir trouver rapidement si le filtre est déjà stocké. Depuis que je le normalise, je "sais" que le XML (par exemple) pour des filtres logiquement équivalents serait identique. Donc, quand quelqu'un va à PUT ou insère un nouveau filtre, je vérifie le hachage pour voir s'il a déjà été stocké. Il se peut que j'en récupère plus d'un (les hachages peuvent entrer en collision, bien sûr), je vais donc devoir vérifier les charges XML réelles pour voir si elles correspondent.

Si les filtres correspondent, je renvoie une référence au filtre existant. Sinon, j'en créerais un nouveau et le renverrais.

Je ne voudrais pas non plus permettre un filtre UPDATE/POST. Puisque je distribue des références à ces filtres, je les rendrais immuables pour que les références puissent rester valables. Si je voulais un filtre par "rôle", disons le filtre "extraire tous les médicaments expirés", je créerais une ressource "filtre nommé" qui associe un nom à une instance de filtre, de sorte que les données de filtre réelles puissent être modifiées, mais la nom reste le même.

Remarquez également que lors de la création, vous êtes dans une situation de concurrence critique (deux demandes essayant de créer le même filtre), vous devez donc en tenir compte. Si votre système a un volume de filtre élevé, cela pourrait constituer un goulot d'étranglement.

J'espère que cela clarifie le problème pour vous.

78
Will Hartung

Pour être reposant, toutes ces informations doivent-elles être incluses dans chaque demande?

Si le serveur envoie (ou reçoit) trop d’informations, il est probable qu’une ou plusieurs ressources ne soient pas encore identifiées.

La première et la plus importante étape dans la conception d’un système RESTful consiste à identifier et à nommer vos ressources. Comment feriez-vous cela pour votre système?

De votre description, voici un ensemble de ressources possibles:

  • Utilisateur - un utilisateur du système (peut-être un médecin ou un patient (?) - Rôle pourrait devoir être exposé en tant que ressource ici)
  • Médicament - le contenu de la bouteille, mais cela peut aussi représenter le type de bouteille (quantité et contenu), ou peut-être une bouteille en particulier, selon que vous soyez une pharmacie ou juste un service d'assistance .
  • Maladie - l'affection pour laquelle un Patient pourrait vouloir prendre un Médication .
  • Patient - une personne susceptible de prendre un médicament
  • Recommandation - a Médicament qui pourrait être bénéfique pour a Patient sur la base d'un Maladie dont ils souffrent.

Ensuite, vous pouvez rechercher des relations entre les ressources;

  • Utilisateur a et appartient à plusieurs Rôles
  • Médication a et appartient à plusieurs Maladies
  • Maladie a beaucoup Recommandations .
  • Patient a et appartient à plusieurs médicaments et maladies (pauvre)
  • Patient a plusieurs Recommandations
  • Recommandation a un Patient et a un Maladie

Les détails ne sont probablement pas adaptés à votre problème, mais l’idée est simple: créez un réseau de relations entre vos ressources.

À ce stade, il peut être utile de réfléchir à la structure d'URI, mais gardez à l'esprit que les API REST _ doivent être pilotées par l'hypertexte }:

# view all Recommendations for the patient
GET http://server.com/patients/{patient}/recommendations

# view all Recommendations for a Medication
GET http://servier.com/medications/{medication}/recommendations

# add a new Recommendation for a Patient
PUT http://server.com/patients/{patient}/recommendations

Comme il s’agit de REST, vous passerez le plus clair de votre temps définition des types de support } utilisé pour transférer les représentations de vos ressources entre le client et le serveur.

En exposant davantage de ressources, vous pouvez réduire la quantité de données à transférer lors de chaque demande. Notez également qu'il n'y a pas de paramètres de requête dans les URI. Le serveur peut être aussi dynamique que nécessaire pour garder une trace de tout, et chaque demande peut être entièrement autonome.

13
Rich Apodaca

REST est destiné aux API, pas aux applications (typiques). N'essayez pas de scinder une interaction fondamentalement avec état dans un modèle sans état simplement parce que vous en avez entendu parler sur wikipedia.

Pour être reposant, toutes ces informations doivent-elles être incluses dans chaque demande? Cela semble entraîner une surcharge considérable sur le réseau. En outre, les restrictions sur la longueur des URL, du moins pour GET, ne rendraient-elles pas cela impossible?

La taille des paramètres est généralement insignifiante comparée à la taille des ressources envoyées par le serveur. Si vous utilisez des paramètres si importants qu'ils constituent une charge réseau, placez-les sur le serveur une fois puis utilisez-les en tant que ressources.

Il n'y a pas de restriction significative sur la longueur de l'URL - si votre serveur a une telle limite, mettez-la à niveau. Il a probablement des années et regorge de failles de sécurité.

11
John Millikin

Non, tout cela ne doit pas nécessairement figurer dans chaque demande.

Chaque ressource (médicament, historique du patient, etc.) doit avoir un URI canonique qui l'identifie de manière unique. Dans certaines applications (par exemple, celles basées sur Rails), cela ressemblera à "/ patients/1234" ou "/ drug/5678", mais le format de l'URL n'a pas d'importance.

Un client ayant précédemment obtenu l'URI d'une ressource (par exemple, à partir d'une recherche ou d'un lien incorporé dans une autre ressource) peut le récupérer à l'aide de cet URI.

5
Licky Lindsay

Travaillez-vous sur une API RESTful que d'autres applications utiliseront pour rechercher vos données? Ou créez-vous une application Web axée sur l'utilisateur final dans laquelle les utilisateurs se connecteront et effectueront ces recherches?

Si vos utilisateurs se connectent, cela signifie que vous disposez déjà d'un type de cookie de session pour conserver l'état de connexion. Je voudrais aller de l'avant et créer un objet de session qui contient tous les filtres de recherche. Si un utilisateur n'a pas défini de filtres, cet objet sera vide.

Voici un excellent article de blog sur l'utilisation de GET vs POST. Il mentionne une limite de longueur d'URL définie par Internet Explorer de 2 048 caractères. Vous souhaitez donc utiliser POST pour les requêtes longues.

http://carsonified.com/blog/dev/the-definitive-guide-to-get-vs-post/

0
CoderDennis