DISEÑO DE API REST: obtener un recurso a través de REST con diferentes parámetros pero el mismo patrón de URL

92

Tengo una pregunta relacionada con el diseño de URL REST. Encontré algunas publicaciones relevantes aquí: Diferentes representaciones RESTful del mismo recurso y aquí: URL RESTful para OBTENER recursos por diferentes campos, pero las respuestas no son muy claras sobre cuáles son las mejores prácticas y por qué. He aquí un ejemplo.

Tengo URL REST para representar el recurso de "usuarios". Puedo OBTENER un usuario con una identificación o con una dirección de correo electrónico, pero la representación de la URL sigue siendo la misma para ambos. Al revisar muchos blogs y libros, veo que la gente ha estado haciendo esto de muchas maneras diferentes. Por ejemplo

leer esta práctica en un libro y en algún lugar de stackoverflow (parece que no puedo encontrar el enlace de nuevo)

GET /users/id={id}
GET /users/email={email}

lee esta práctica en muchos blogs

GET /users/{id}
GET /users/email/{email}

Los parámetros de consulta se usan normalmente para filtrar los resultados de los recursos representados por la URL, pero también he visto que esta práctica se usa

GET /users?id={id}
GET /users?email={email}

Mi pregunta es, de todas estas prácticas, ¿cuál tendría más sentido para los desarrolladores que consumen las apis y por qué? Creo que no hay reglas escritas en piedra cuando se trata de diseños de URL REST y convenciones de nomenclatura, pero solo quería saber qué ruta debo tomar para ayudar a los desarrolladores a comprender mejor las apis.

¡Toda la ayuda apreciada!

shahshi15
fuente
1
Sé que esto es un poco antiguo, pero al buscar recursos similares me tropiezo con esta pregunta y con la que estabas buscando. Creo que es este stackoverflow.com/a/9743414/468327
Joel Berger

Respuestas:

104

En mi experiencia, GET /users/{id} GET /users/email/{email}es el enfoque más común. También esperaría que los métodos devuelvan un 404 Not Found si un usuario no existe con el ido email. Tampoco me sorprendería verlo GET /users/id/{id}(aunque en mi opinión, es redundante).

Comentarios sobre los otros enfoques

  1. GET /users/id={id} GET /users/email={email}
    • No creo haber visto esto, y si lo hubiera visto, sería muy confuso. Es casi como si estuviera tratando de imitar parámetros de consulta con parámetros de ruta.
  2. GET /users?id={id} GET /users?email={email}
    • Creo que dio en el clavo cuando mencionó el uso de parámetros de consulta para el filtrado.
    • ¿Sería siempre tiene sentido para llamar a este recurso con tanto una idy una email(por ejemplo GET /users?id={id}&email={email})? Si no, no usaría un método de recurso único como este.
    • Esperaría que este método para recuperar una lista de usuarios con parámetros de consulta opcionales para el filtrado, pero no esperaría id, emailni ningún identificador único entre los parámetros. Por ejemplo: GET /users?status=BANNEDpodría devolver una lista de usuarios prohibidos.

Consulte esta respuesta de una pregunta relacionada.

DannyMo
fuente
Gracias por la aportación. De hecho tienes razón. Parece que leí el n. ° 1 en el libro "RESTful java with Jax-rs" pero algo fuera de contexto. Pero recuerdo muy claramente haber leído eso como respuesta a una de las preguntas sobre stackoverflow en sí (créanme cuando digo que estoy tratando de encontrar ese enlace para demostrárselo a mis colegas también :)) y el # 2 es exactamente lo que era. buscando. Algunos comentarios sobre el diseño. ¡Gracias!
shahshi15
PD: No estoy seguro de si publicar esto va en contra de la regla aquí, pero probablemente también puedas arrojar algo de luz sobre alguna de mis otras preguntas. ¿Por favor? :) stackoverflow.com/questions/20508008/…
shahshi15
solo para aclarar con respecto a su declaración de redundancia /users/id/{id}, esto permite una funcionalidad extendida, simplemente permite acceder a un recurso a través de varios identificadores (id, guid, name). también respondido aquí
Daniel Dubovski
4
Mi respuesta sería que la forma adecuada de hacer referencia a un usuario específico, por ejemplo, sería / users / {id}. Si quisiera encontrar usuarios por una dirección de correo electrónico en particular que no sea el identificador único para los usuarios, lo haría / users? Email = {email} ya que es una forma de filtrar la colección de usuarios, no identificar un recurso específico por su recurso identificador como / users / {id} es.
Kevin M
¡Gran respuesta! Pulgar hacia arriba. Lo único que recomendaría hacer es también cambiar un poco su API, ya que REST debería estar centrado en el nombre, por lo que sus asignaciones de recursos deberían ser algo como: GET /user/1234y noGET /users/123
Sammy
38

Mirando esto pragmáticamente, tienes una colección de usuarios:

/users   # this returns many

Cada usuario tiene una ubicación de recursos dedicada:

/users/{id}    # this returns one

También tiene varias formas de buscar usuarios:

/users?email={email}
/users?name=*bob*

Dado que todos estos son parámetros de consulta para / users, todos deberían devolver listas ... incluso si es una lista de 1.

Escribí una publicación de blog sobre el diseño pragmático de API RESTful aquí que habla de esto, entre otras cosas, aquí: http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api

Vinay Sahni
fuente
Gracias por la respuesta vinay. Pero si pensamos en esto desde otra perspectiva, {email} al igual que {id} SIEMPRE devolverá un resultado. Si realmente estamos buscando, ¿por qué no usar? Id = {id} tan bien como para el correo electrónico? Me preguntaba si hay alguna forma de ser consistente. PD: nuevo en stackoverflow, no sabía que solo puedo dar una respuesta. ¡Pero gracias por tu respuesta! Lo aprecio. Quizás también puedas ayudarme con este: stackoverflow.com/questions/20508008/…
shahshi15
7
Aquí es donde entra en juego el aspecto de "diseño" del diseño de API. Su recurso debe tener una ubicación dedicada. ¿Dónde está la ubicación? ¿La ubicación es por identificación? Si es así, entonces no "busca" por id, "obtiene" por id. Pero buscas por todo lo demás. Por supuesto, aquí no hay una regla estricta. Es solo una perspectiva :)
Vinay Sahni
6
También diría que de esta manera su API es más estable. Realmente sabe que su identificación es única, pero ¿está realmente seguro de la dirección de correo electrónico? Incluso si es único, probablemente no sea permanente, ya que el usuario puede cambiarlo. Entonces / users? Email = {email} deja en claro que se trata de una consulta y no de acceso a un recurso con un identificador permanente.
lex82
Creo que esta es en realidad la respuesta más correcta. El filtrado de una colección por dirección de correo electrónico podría basarse en comodines, por lo que no siempre devolverá un solo resultado.
Kevin M
@VinaySahni ¿Qué dirías sobre un patrón como: /user?by=email&email="[email protected] "/ user? By = id & id =" xashkhx "/ users? FilterA =" a "& filterB =" b "(plural para múltiples usuarios)
Shasak
2

Sobre los recursos del usuario

en la ruta /userssiempre obtendrá una colección de recursos de usuario devueltos.

en el camino /users/[user_id]puede esperar que sucedan varias cosas:

  1. debe obtener un recurso singleton que represente el recurso del usuario con su identificador [user_id] o
  2. una respuesta 404 no encontrada si no existe ningún usuario con id [user_id] o
  3. un 401 prohibido si no se le permite acceder al recurso de usuario solicitado.

Cada singleton se identifica de forma única por su ruta e identificador y usted los usa para encontrar el recurso. No es posible utilizar varias rutas para el singleton.

Puede consultar la ruta /userscon parámetros de consulta ( GETParámetros). Esto devolverá una colección con usuarios que cumplan con los criterios solicitados. La colección que se devuelve debe contener los recursos del usuario, todos con su ruta de recursos de identificación en la respuesta.

Los parámetros pueden ser cualquier campo presente en los recursos de la colección; firstName, lastName,id

Sobre el correo electrónico

El correo electrónico puede ser un recurso o una propiedad / campo del recurso del usuario.

- Correo electrónico como propiedad del usuario:

Si el campo es una propiedad del usuario, la respuesta del usuario se vería así:

{ 
  id: 1,
  firstName: 'John'
  lastName: 'Doe'
  email: '[email protected]'
  ...
}

Esto significa que no hay punto final especial para correos electrónicos, pero ahora se puede encontrar un usuario por su correo electrónico mediante el envío de petición siguiente: /[email protected]. Que (asumiendo que los correos electrónicos son exclusivos de los usuarios) devolverían una colección con un elemento de usuario que coincide con el correo electrónico.

- Correo electrónico como recurso:

Pero si los correos electrónicos de los usuarios también son recursos. Luego, podría crear una API donde /users/[user_id]/emailsdevuelva una colección de direcciones de correo electrónico para el usuario con id user_id. /users/[user_id]/emails/[email_id]devuelve el correo electrónico del usuario con user_id y ['email_id']. Lo que use como identificador depende de usted, pero yo me quedaría con un número entero. Puede eliminar un correo electrónico del usuario enviando una DELETEsolicitud a la ruta que identifica el correo electrónico que desea eliminar. Entonces, por ejemplo, DELETEon /users/[user_id]/emails/[email_id]eliminará el correo electrónico con email_id que es propiedad del usuario con user_id. Lo más probable es que solo ese usuario pueda realizar esta operación de eliminación. Otros usuarios obtendrán una respuesta 401.

Si un usuario solo puede tener una dirección de correo electrónico, puede seguir. /users/[user_id]/email Esto devuelve un recurso singleton. El usuario puede actualizar su dirección de correo electrónico PUTmarcando la dirección de correo electrónico en esa url.

Marchitar
fuente