7 votes

Conception de l'API REST : un point de terminaison avec une logique if/else ou deux points de terminaison distincts basés sur les rôles.

J'ai un problème de conception/version d'API. Disons que j'ai un point de terminaison /api/customers qui récupère tous les clients (ignorez la pagination). Mais il y a un hic : si un client régulier user accède à ce point de terminaison, il n'obtiendra que les clients créés par cet utilisateur et personne d'autre (je peux vérifier le jeton d'accès et le sous-champ pour déterminer qui a envoyé la requête). Autre cas d'utilisation : si un admin accède à ce point de terminaison, ils devraient obtenir TOUS les clients, peu importe qui les a acquis.

Ma question est la suivante : du point de vue de la conception de l'API, est-il préférable d'avoir une if/else vérifier le rôle dans le contrôleur API lui-même pour déterminer si je renvoie TOUS les clients (admin) ou des clients spécifiques (utilisateur), OU si je dois différencier les points de terminaison pour l'utilisateur et l'admin ? Par exemple, le point d'accès réservé à l'administrateur pour tous les clients serait le suivant /api/admin/customers et les utilisateurs réguliers peuvent toujours accéder à leurs /api/customers ?

2voto

VoiceOfUnreason Points 849

Dans REST, il est normal d'avoir plusieurs ressources qui partagent les mêmes représentations.

Par exemple, la "version préférée des auteurs" d'un article universitaire est un mappage dont la valeur évolue dans le temps, alors qu'un mappage vers "l'article publié dans les actes de la conférence X" est statique. Il s'agit de deux ressources distinctes, même si elles correspondent toutes deux à la même valeur à un moment donné. Cette distinction est nécessaire pour que les deux ressources puissent être identifiées et référencées indépendamment. Un exemple similaire dans le domaine de l'ingénierie logicielle est l'identification séparée d'un fichier de code source contrôlé par version lorsqu'on se réfère à la "dernière révision", au "numéro de révision 1.2.7" ou à la "révision incluse dans la version Orange". -- Fielding, 2000

Il est parfaitement cohérent avec cette approche d'avoir une ressource pour "tous les utilisateurs", et une autre pour "les utilisateurs créés par Bob".

Là où les choses se compliquent, c'est dans le cas où l'on veut utiliser la fonction même pour fournir des représentations différentes. En d'autres termes, lorsque Alice regarde "users created by me", elle voit "users created by Alice", et lorsque Bob regarde "users created by me", il voit "users created by Bob".

Une possibilité est de faire en sorte que les "utilisateurs créés par moi" redirigent vers la ressource appropriée. Cela fonctionne, pour les valeurs de "fonctionne" qui permettent des allers-retours supplémentaires lorsque la ressource de destination n'est pas déjà dans le cache local.

Dans HTTP/2, le push serveur peut vous épargner une partie de ces allers-retours.

Les règles pour caches partagés devrait vous protéger contre l'envoi de la vue d'Alice de la ressource "moi" à Bob, et vice versa, mais il est utile de connaître la signification des différents en-têtes afin de ne pas désactiver cette protection par inadvertance.

Le fait d'avoir des ressources différentes peut poser un problème dans certains contextes de "lecture de ses propres écritures", car les caches ne sauront pas qu'une requête non sécurisée a invalidé les données de l'utilisateur. les deux ressources. Bob crée un nouvel utilisateur via un POST vers "users created by me", et l'entrée de cache correspondante est invalidée... mais "all users" est une clé de cache différente, et n'est pas invalidée. Donc si Bob regarde la vue "all users", il peut voir une copie précédemment mise en cache sans les changements qu'il vient de voir dans sa propre vue.

Dans certains cas, il peut être judicieux d'envisager des sous-ressources.

/api/customers
/api/customers#created-by-Alice
/api/customers#created-by-Bob

Mais si vous essayez de réduire la quantité de données non pertinentes échangées, ce n'est pas une bonne solution.

1voto

Nelson Points 21

Ce devrait être le même point d'arrivée. Sinon, chaque front-end qui appelle votre API doit avoir la même logique pour déterminer le rôle et le mappage du point de terminaison.

1voto

Allen Haley Points 951

Cela dépend de votre projet.

  1. S'il n'y a que deux cas, comme vous l'avez mentionné.
    • obtenir uniquement les clients créés par cet utilisateur pour regular utilisateurs
    • obtenir tous les clients pour admin utilisateurs

alors, il serait préférable d'utiliser un seul point d'accès en ajoutant un intergiciel pour vérifier le rôle actuel de l'utilisateur.

  1. Si vous envisagez d'étendre votre projet. Par exemple, si admin les utilisateurs ont également besoin d'obtenir les clients créés par cet utilisateur, il serait préférable de créer 2 points de terminaison. un pour tous les clients, un autre pour les clients de l'utilisateur actuel. comme api/customers/all , api/customers/me

1voto

johnr Points 153

Je pense que /api/customers est parfait pour les cas mentionnés. C'est analogue à une requête de page web vers index.html qui renvoie un contenu différent à différents utilisateurs.

Si vous voulez l'étendre (par exemple, Alice demandant la liste de Bob), vous pourriez supporter des paramètres de requête optionnels :

/api/customers?accessibleTo=bob
/api/customers?createdBy=bob

Cela nécessiterait probablement un contrôle d'autorisation (Alice a-t-elle accès à la liste de Bob ?), renvoyant 403 (ou 404, selon votre philosophie) en cas de non autorisation.

N'oubliez pas non plus la mise en cache. Évitez que deux requêtes à la même URL (/api/customers) pour des utilisateurs différents aient pour résultat qu'un utilisateur obtienne la liste de l'autre.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X