2009-05-08 4 views
43

Pour le site sur lequel nous travaillons, nous sommes en train d'améliorer nos URL pour un type de ressource - en particulier, nous nous éloignons des ID numériques vers des chaînes descriptives uniques. Un exemple similaire serait de passer de l'identification des utilisateurs par ID de base de données numérique à leur identification par nom d'utilisateur (pas notre cas spécifique, mais analogue). Ainsi, une URL pour accéder aux informations d'un utilisateur utilisé pour ressembler à:REST - Prise en charge de plusieurs identifiants possibles

/users/48573 

Et maintenant il ressemble à

/users/thisisausername. 

Le seul problème est que nous avons encore besoin de pouvoir les récupérer par ID numériques en quelque sorte , pour les utilisateurs hérités de l'API. Nous n'avons pas besoin des URL REST elles-mêmes pour rediriger (par exemple, /users/48573 ne doit pas rediriger vers /users/thisisausername), nous avons juste besoin d'une méthode pour obtenir les bonnes données en utilisant l'ancien identifiant. La solution devrait soit fournir un moyen alternatif d'accéder aux informations de l'utilisateur (ce qui inclut commodément le nouvel identifiant, le nom d'utilisateur) par ID, soit accéder uniquement au nom d'utilisateur par ID. Certaines solutions possibles peuvent être:

  • L'utilisation d'un noeud pour spécifier une autre méthode d'identification, par ex. Utilisation d'un paramètre de requête pour spécifier une autre méthode d'identification, par ex. /users/48573?fetchby=id ou /users/48573?byid=true
  • Traiter le nom d'utilisateur par ID comme une autre ressource, par ex. /identifiers/username/48573

Lequel (le cas échéant) est le plus proche du bon REST? Comment traiteriez-vous le problème?

+4

J'ai fini par mettre en œuvre l'accès par des champs non-principal-identifiant comme une recherche. Cette solution permet d'extraire plusieurs types de ressources via plusieurs champs, tout en n'en conservant qu'un comme identificateur principal. Par souci de cohérence, les API "search" renvoient des listes. Ainsi, la manière officielle d'accéder à un utilisateur est: /user/thisisausername et l'accès par ID, nous avons: /utilisateurs id = 48573 De même, nous pourrions effectuer une recherche sur un certain nombre de domaines , dans: /users? firstName = Kelly Source d'inspiration: http://jwyseur.blogspot.com/2008/12/uri-design-for-rest.html (voir "recherche d'une ressource") –

+0

Alors vous avez mis en cache sur la mise en cache? J'ai le même problème que vous, mais je ne peux pas les résoudre via des paramètres de requête qui suppriment l'un des principaux avantages d'une API REST. J'aime votre première suggestion à puces ... – HDave

Répondre

10

Votre première option est probablement la meilleure.

Recherche d'utilisateurs par ID:

/users/id/48573 

Recherche d'utilisateurs par nom court:

/users/name/thisisausername 

Si elles quittent que le paramètre de chemin, vous pouvez toujours défaut à votre nouveau format de nom d'utilisateur court.

Une autre option que je l'ai vu un peu est d'utiliser des paramètres de la requête comme suit:

/users?id=48573 
/users?name=thisisausername 

Je pense que le premier semble un peu plus propre et plus lisible.

+0

court et précis, parfait! –

-3

je considère qualifier la chaîne avec un suffixe en option:

/users/48573/id 

/users/48573/name 

Si vous recevez une chaîne sans le suffixe:

/users/48573 

vous vérifiez la chaîne et voir si elle est un ID ou Nom.

Si vous obtenez seulement une pièce d'identité valide, mais pas un nom alors il est une recherche par ID équivalent à:

/users/48573/id 

Si vous obtenez seulement un nom à l'époque, il y a une recherche par nom équivalent à:

/users/48573/name 

Si vous pouvez récupérer la valeur par ID ou le nom puis vous revenez d'une erreur de réponse 300 et le retour des liens vers les deux possiblités au client:

/users/48573/id 

/users/48573/name 

Les consommateurs existants continuent de fonctionner «tels quels», à l'exception de l'apparition occasionnelle de paires ID/nom en double où ils reçoivent la nouvelle erreur de réponse 300.

+7

Ce n'est pas du tout REST. Ceci est juste RPC. – aehlke

-3

Votre API n'est pas RESTful si c'est un problème. Pour quote Roy Fielding:

Une API REST ne doit pas définir de noms ou de hiérarchies de ressources fixes (un couplage évident entre le client et le serveur). Les serveurs doivent avoir la liberté de contrôler leur propre espace de noms. Au lieu de cela, autorisez les serveurs à indiquer aux clients comment créer des URI appropriées, comme dans les formulaires HTML et les modèles d'URI, en définissant ces instructions dans les types de média et les relations de liaison. [L'échec ici implique que les clients supposent une structure de ressources due à des informations hors bande, comme un standard spécifique au domaine, qui est l'équivalent orienté vers les données du couplage fonctionnel de RPC]. Une API REST doit être entrée sans aucune connaissance préalable au-delà de l'URI (marque-page) et de l'ensemble des types de média normalisés appropriés pour le public visé (c'est-à-dire que tout client susceptible d'utiliser l'API devrait comprendre). . À partir de ce moment, toutes les transitions d'état d'application doivent être pilotées par la sélection par le client des choix fournis par le serveur qui sont présents dans les représentations reçues ou impliqués par la manipulation de ces représentations par l'utilisateur. Les transitions peuvent être déterminées (ou limitées par) les connaissances du client sur les types de média et les mécanismes de communication de ressources, qui peuvent tous deux être améliorés à la volée (par exemple, code à la demande). [L'échec ici implique que les informations hors bande conduisent l'interaction à la place de l'hypertexte.]

+1

Ceci n'est pas une réponse à sa question. Une API REST peut prendre en charge HATEOAS toute la journée, mais les développeurs de serveurs doivent encore s'inquiéter des problèmes de conception d'URI. – HDave

28

Je pense que l'ajout d'un segment/préfixe est la meilleure réponse. Comme ce sont des clés secondaires uniques, ce n'est pas la même chose que search (qui renvoie un ensemble d'éléments), donc l'utilisation de paramètres de requête (qui ne sont pas mis en cache) ne semble pas être le meilleur choix.

Personnellement, je prévois d'utiliser un préfixe de segment de chemin délimité par "=", comme "name =" ou "email =":

user/123456 
user/name=john.doe 
user/[email protected] 

Ceci est fonctionnellement équivalent à l'ajout d'un segment de chemin (par exemple " user/name/john.doe "), mais il me semble que cela ressemble plus au modèle conceptuel. Bien sûr, ceci est un détail insignifiant, puisque les API RESTful ne devraient pas spécifier une structure d'URI fixe de toute façon.

Ne pas utiliser les paramètres de la requête permet également des sous-ressources pour accéder naturellement:

user/name=john.doe/inbox/df87bhJXrg63 

Cadres comme JAX-RS de support Java en utilisant tous les délimiteur que vous voulez:

@GET 
@Path("user/{id}") 
User getUser(@PathParam("id") UUID id); 

@GET 
@Path("user/name={name}") 
User getUserByName(@PathParam("name") String name); 

@GET 
@Path("user/email={email}") 
User getUserByEmail(@PathParam("email") String email); 
+2

C'est une approche intéressante que je n'avais jamais vue auparavant. Il résout bien le problème, mais je ne peux pas passer outre le fait que voir le signe égal en dehors des paramètres de requête semble être source de confusion ... – HDave

+4

De plus, ce n'est pas très reposant. quoi, exactement, est le nom de la collection = john.doe? Pour moi, il devrait être un paramètre de la matrice: utilisateur; nom = john.doe/inbox/df87 ... http://blog.2partsmagic.com/restful-uri-design/ – Maladon

+0

@Maladon Je pense que vous êtes manquer le point. 'name = john.doe' ne devrait pas être une collection, puisque' name' est un identifiant unique. Nous recherchons exactement un objet ou une erreur. 'user; name = john.doe' ressemble à une recherche. Je m'attends à ce qu'il renvoie la même représentation que 'user', qui est une collection. –

0

Tout à fait une vieille question, mais J'ai eu le même et finalement trouvé la solution: utilisez regex dans votre chemin param.

Voilà comment je codé ce cas d'utilisation

@GET 
@Path("/{id : \\d+}") 
@Produces(APPLICATION_JSON) 
public Response getById(@PathParam("id") long id) { 
<<your code>> 
} 

@GET 
@Path("/{name}") 
@Produces(APPLICATION_JSON) 
public Response getByName(@PathParam("name") String name) { 
<<your code>> 
} 
+1

Que faire si le nom de l'utilisateur est numérique? Si un utilisateur peut choisir librement son nom et décide d'utiliser uniquement un numéro, votre approche échoue. –

+0

il échoue ou non. Vous devez effectuer des contrôles sur la création (POST) et refuser le nom numérique uniquement. – Javathought

+0

@@ POST public Réponse create (@Valid User user) et d'annoter votre modèle avec la validation du bean. Exemple: doit commencer par une lettre majuscule, 50 caractères max @@ Motif (regexp = "[AZ] [a-zA-Z_0-9-] {0,49}", message = "nom invalide") privé Nom de la chaîne – Javathought