web-dev-qa-db-fra.com

API RESTful - Conception de sous-ressources

Je conçois une API RESTful et j'ai rencontré un problème lié aux sous-ressources.

Je vois d'autres API utilisant une URL complète pour fonctionner sur des sous-ressources. Prenons l'exemple où Company has Departments et Department has Employees.

Au début, j'ai pensé à implémenter toutes les URL possibles. Résultat sur les éléments suivants:

Approche A

01. ### COMPANY URLS ###
02. DELETE /companies/{companyId}
03. GET    /companies/{companyId}
04. POST   /companies
05. PUT    /companies/{companyId}
06. 
07. ### DEPARTMENT URLS ###
08. DELETE /companies/{companyId}/departments/{departmentId}
09. GET    /companies/{companyId}/departments/{departmentId}
10. POST   /companies/{companyId}/departments
11. PUT    /companies/{companyId}/departments/{departmentId}
12. DELETE /departments/{departmentId}
13. GET    /departments/{departmentId}
14. PUT    /departments/{departmentId}
15. 
16. ### EMPLOYEE URLS ###
17. DELETE /companies/{companyId}/departments/{departmentId}/employees/{employeeId}
18. GET    /companies/{companyId}/departments/{departmentId}/employees/{employeeId}
19. POST   /companies/{companyId}/departments/{departmentId}/employees
20. PUT    /companies/{companyId}/departments/{departmentId}/employees/{employeeId}
21. DELETE /departments/{departmentId}/employees/{employeeId}
22. GET    /departments/{departmentId}/employees/{employeeId}
23. POST   /departments/{departmentId}/employees
24. PUT    /departments/{departmentId}/employees/{employeeId}
25. DELETE /employees/{employeeId}
26. GET    /employees/{employeeId}
27. PUT    /employees/{employeeId}

Comme vous pouvez le voir, il existe de nombreuses URL qui font la même chose. Exemple: 08 est dupliqué de 12; 09 est doublé de 13; 17 est dupliqué de 21 et 25 ...

Je veux supprimer la duplication mais garder la cohérence. Donc, repensé l'API avec un principe à l'esprit sup-resources are fine but sub-sub-resources are not. Ce qui s'est traduit par ce qui suit:

Approche B

01. ### COMPANY URLS ###
02. DELETE /companies/{companyId}
03. GET    /companies/{companyId}
04. POST   /companies
05. PUT    /companies/{companyId}
06. 
07. ### DEPARTMENT URLS ###
08. DELETE /departments/{departmentId}
09. GET    /departments/{departmentId}
10. GET    /companies/{companyId}/departments
11. POST   /companies/{companyId}/departments
12. PUT    /departments/{departmentId}
13. 
14. ### EMPLOYEE URLS ###
15. DELETE /employees/{employeeId}
16. GET    /employees/{employeeId}
17. GET    /departments/{departmentId}/employees
18. POST   /departments/{departmentId}/employees
19. PUT    /employees/{employeeId}

Mes questions

Q1. Approche B est-il considéré comme RESTful? (Je suppose que oui)

Q2. Y a-t-il des pièges Approche B Je devrais considérer, en supposant que la documentation est également fournie?

Points bonus si vous pointez vers d'autres API en suivant approche B.

[~ # ~] modifier [~ # ~]

Elad Tabak a présenté de bonnes idées.

J'adore certaines API en utilisant Approche B:

https://developers.google.com/youtube/v3/docs/

https://developer.github.com/guides/getting-started/

https://dev.Twitter.com/rest/public

20
Rafa

Les deux approches peuvent être considérées comme RESTful, à condition de ne pas rompre les contraintes REST définies dans le chapitre 5 de la thèse de Roy Thomas Fielding:

Je ne vois pas de pièges majeurs dans les deux approches, mais je préférerais le Approche B plutôt que le Approche A: les URL sont plus courtes, plus faciles à retenir et peu nombreuses des paramètres sont requis.


Points bonus: Spotify et Facebook Les API suivent cette approche. Bien sûr, il existe d'autres API, mais ce sont celles qui me sont venues à l'esprit.

7
cassiomolin

L'approche de conception soulève quelques questions que vous devez considérer lors du choix entre les deux:

Dépendance à l'existence

Dans A , il est très intuitif que lorsque vous SUPPRIMEZ une entreprise, vous supprimez également toutes ses sous-ressources - départements et employés. Dans B , l'utilisateur de l'API doit réfléchir un instant à une telle action - dois-je invoquer la suppression sur tous les employés ou suffit-il de supprimer l'entreprise ? bien sûr, cela peut être documenté, mais quand même, ce n'est pas simple.

A ont un avantage ici, car c'est très clair - lorsque vous supprimez une ressource, vous supprimez toutes ses sous-ressources.

Point final de l'opération

Une autre question qu'il soulève - comment mettre à jour une entité? à partir de quel point final?

Si je veux supprimer un employé, est-ce suffisant pour mettre à jour l'entreprise avec un nouvel ensemble d'employés? ou dois-je SUPPRIMER l'employé?

Ou dites que je veux changer d'employé d'une entreprise à une autre. En B , théoriquement, je peux mettre à jour l'employé avec le champ de l'entreprise et en finir avec cela. Dans A il n'y a pas une telle façon ...

A a un avantage où il est très simple de faire une action - CRUD sur l'URL de l'entité. B oblige l'utilisateur de l'API à s'arrêter et se demande quelle action il peut faire sur quelle URL.

Mais en même temps, B a l'avantage que changer la "parentalité" d'une entité est plus facile (dans les cas où cela est pertinent).

Validation

Dans A , vous devez valider la correspondance des arguments URL, car l'utilisateur peut fournir l'ID de l'employé avec la mauvaise entreprise ou le mauvais service. Dans B il n'y a pas un tel problème.

5
Elad Tabak
  1. REST ne dit rien sur la conception d'URL. Tout schéma d'URL que vous proposez est RESTful. Vous devriez demander si c'est un bon design. Et oui, la seconde approche est hautement préférable à la première. Le premier est une tonne de bruit pour les clients et un énorme problème de maintenance pour le propriétaire. Cela limite également la flexibilité future.

  2. Il n'y a pas d'écueils importants à ma connaissance, tant que vous documentez clairement comment utiliser les points de terminaison. Par exemple, il est typique que les points de terminaison imbriqués ne renvoient que les éléments associés, et que SUPPRIMER un élément imbriqué ne supprime que l'association et non l'élément lui-même. Ce type de comportement doit être documenté d'une manière ou d'une autre.

Points bonus: demander des ressources externes est hors de portée.

5
Eric Stein

Cela peut donc sembler un peu fou, mais il n'existe pas de "sous-ressource" dans HTTP/REST.

Dans votre modèle de domaine, un service ne peut exister sans une entreprise.

Maintenant, dans votre API, vous exposez une représentation json d'une entreprise à /companies/{companyId} et une représentation json d'un département à /companies/{companyId}/departments/{departmentId}.

Ce sont deux "ressources". Une ressource, dans la terminologie HTTP/REST, signifie simplement une chose vers laquelle pointe une URL. C'est donc la "représentation json d'une entreprise" et non l'entreprise elle-même.

La conception d'URL est en soi un peu une impasse en soi - les URL elles-mêmes peuvent ressembler à TOUT, peu importe qu'elles soient lisibles ou non. Les développeurs font des demandes, en sélectionnant les URL en fonction des noms d'opération dans la documentation *. Essayer d'ajouter du sens aux URL elles-mêmes devient délicat rapidement et peut en fait ajouter de la confusion au fil du temps.

Mieux vaut passer du temps sur la documentation, plutôt que d'essayer de laisser les gens inférer des choses sur le comportement du domaine.

* ou hypermédia (par exemple https://github.com/kevinswiber/siren )

4
mcintyre321