¿Diseñar / iniciar sesión o / registrar recursos de manera RESTOSA?

122

Estaba diseñando una aplicación web y luego me detuve a pensar en cómo debería diseñarse mi api como un servicio web RESTful. Por ahora, la mayoría de mis URI son genéricos y pueden aplicarse a varias aplicaciones web:

GET  /logout   // destroys session and redirects to /
GET  /login    // gets the webpage that has the login form
POST /login    // authenticates credentials against database and either redirects home with a new session or redirects back to /login
GET  /register // gets the webpage that has the registration form
POST /register // records the entered information into database as a new /user/xxx
GET  /user/xxx // gets and renders current user data in a profile view
POST /user/xxx // updates new information about user

Tengo la sensación de que estoy haciendo mucho mal aquí después de hurgar en SO y Google.

Comenzando con /logout, quizás ya que realmente no tengo GETnada, puede ser más apropiado para POSTuna solicitud /logout, destruir la sesión y luego GETla redirección. ¿Y debería /logoutquedarse el plazo?

¿Qué pasa con /loginy /register. Podría cambiar /registera, /registrationpero eso no altera el funcionamiento fundamental de mi servicio, si tiene problemas más profundos.

Ahora me doy cuenta de que nunca expongo un /userrecurso. Quizás eso podría utilizarse de alguna manera. Por ejemplo, tomemos al usuario myUser:

foo.com/user/myUser

o

foo.com/user

El usuario final no requiere esa verbosidad adicional en el URI. Sin embargo, ¿cuál es más atractivo visualmente?

Noté algunas otras preguntas aquí en SO sobre este negocio REST, pero realmente agradecería alguna orientación sobre lo que he presentado aquí si es posible.

¡Gracias!

ACTUALIZAR:

También me gustaría algunas opiniones sobre:

/user/1

vs

/user/myUserName
Qcom
fuente

Respuestas:

63

Una cosa destaca en particular como no REST-ful: el uso de una solicitud GET para cerrar la sesión.

(de http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods )

Algunos métodos (por ejemplo, HEAD, GET, OPTIONS y TRACE) se definen como seguros, lo que significa que están destinados únicamente a la recuperación de información y no deben cambiar el estado del servidor. En otras palabras, no deberían tener efectos secundarios, más allá de los efectos relativamente inofensivos como el registro, el almacenamiento en caché, la publicación de anuncios publicitarios o el aumento de un contador web. [...]

El [...] manejo [de las solicitudes GET] por parte del servidor no está técnicamente limitado de ninguna manera. Por lo tanto, la programación descuidada o deliberada puede provocar cambios no triviales en el servidor. Esto se desaconseja, porque puede causar problemas para el almacenamiento en caché web, motores de búsqueda y otros agentes automatizados [...]

En cuanto al cierre de sesión y la redirección, puede hacer que una publicación en su URI de cierre de sesión dé una respuesta 303 que redirija a la página posterior al cierre de sesión.

http://en.wikipedia.org/wiki/Post/Redirect/Get

http://en.wikipedia.org/wiki/HTTP_303

Edite para abordar los problemas de diseño de URL:

"¿Cómo diseño mis recursos?" es una pregunta importante para mi; "¿Cómo diseño mis URL?" es una consideración en dos áreas:

Las URL que verán los usuarios no deben ser demasiado feas y significativas si es posible; Si desea que las cookies se envíen en solicitudes a algún recurso pero no a otros, querrá estructurar sus rutas y rutas de cookies.

Si JRandomUserquiere ver su propio perfil y desea que la URL sea más bonita que foo.com/user/JRandomUsero foo.com/user/(JRandom's numeric user id here), puede crear una URL separada solo para que un usuario vea su propia información:

GET foo.com/profile /*examines cookies to figure out who 
                     * is logged in (SomeUser) and then 
                     * displays the same response as a
                     * GET to foo.com/users/SomeUser.
                     */

Afirmaría ignorancia mucho más fácilmente que sabiduría sobre este tema, pero aquí hay algunas consideraciones sobre el diseño de recursos:

  1. Consumidor: ¿qué recursos están destinados a ser vistos directamente en un navegador, cargados a través de XHR o accedidos por algún otro tipo de cliente?
  2. Acceso / identidad: ¿la respuesta depende de cookies o referentes?
ellisbben
fuente
1
Gran respuesta, gracias! Si fuera a implementar su sugerencia de URL separada ( GET foo.com/profile/), ¿sería parte de, como sugirió momo, la capa de presentación? En otras palabras, ¿qué debería GETdevolver exactamente esa solicitud? ¿Una página web o algún JSON?
Qcom
2
Ah, creo que ahora veo. La respuesta de Momo realmente aclaró las cosas. Por lo que una API REST se construye para permitir a múltiples plataformas GET, POST, PUT, y DELETErecursos. Un sitio web es solo otra plataforma que accede a la API. En otras palabras, el diseño de la URL del sitio web es completamente diferente al diseño de la API RESTful. Por favor dime si sigo equivocado jaja.
Qcom
Sí, convierta su API REST en un conjunto de URL y su sitio web en un conjunto diferente. Luego, la URL de su sitio web debe devolverle el HTML + Javascript apropiado para que la página realice las XmlHttpRequests adecuadas a las URL de su API para que actúe como cliente.
ellisbben
129

RESTful se puede utilizar como una guía para construir URL, y puede crear sesiones y recursos de usuarios :

  • GET /session/new obtiene la página web que tiene el formulario de inicio de sesión
  • POST /session autentica las credenciales contra la base de datos
  • DELETE /session destruye la sesión y redirige a /
  • GET /users/new obtiene la página web que tiene el formulario de registro
  • POST /users registra la información ingresada en la base de datos como un nuevo / usuario / xxx
  • GET /users/xxx // obtiene y muestra los datos del usuario actual en una vista de perfil
  • POST /users/xxx // actualiza nueva información sobre el usuario

Estos pueden ser en plural o en singular (no estoy seguro de cuál es el correcto). Por lo general, lo he usado /userspara una página de índice de usuario (como se esperaba) y /sessionspara ver quién inició sesión (como se esperaba).

El uso del nombre en la URL en lugar de un número ( /users/43vs. /users/joe) generalmente se debe al deseo de ser más amigable con los usuarios o los motores de búsqueda, no por requisitos técnicos. Cualquiera de los dos está bien, pero te recomiendo que seas constante.

Creo que si va con el registro / inicio de sesión / cierre de sesión o sign(in|up|out), no funciona tan bien con la terminología relajante.

ndp
fuente
6
¡Increíble! Me gusta cómo nombraste esos recursos; eso está bastante limpio. Aunque, por lo que he escuchado, ¿no se agrega /newa GET /session/no RESTful? He oído que los verbos normalmente se dejan a los verbos HTTP ( GET, POST, etc).
Qcom
2
@Zach new no es un verbo. En este caso, es un recurso secundario de sesión.
Kugel
¿Cómo determinar qué sesión eliminar en DELETE / session? Curl no envía cookies ni parámetros en la solicitud DELETE. Supongo, ¿solo para usar DELETE / session / sessionId? Otra pregunta es cómo devolver la identificación de la sesión en POST / sesión y en qué formato.
Tvaroh
9
Descansar de hecho es una forma de hacerte infeliz y perder tiempo en cosas que no importan en absoluto.
Jian Chen
6
Personalmente no me gusta la idea de tener rutas que devuelvan el formulario (/ nuevo). Esto rompe la separación entre la vista y la lógica empresarial. Dicho esto, sin las / nuevas rutas, la sugerida parece perfecta.
Scadge
60

Las sesiones no son RESTful

  • Sí, lo sé. Se está haciendo, generalmente con OAuth, pero en realidad las sesiones no son RESTful. No debería tener un recurso / login / logout principalmente porque no debería tener sesiones.

  • Si vas a hacerlo, hazlo DESCANSO. Los recursos son sustantivos y / login y / logout no son sustantivos. Yo iría con / session. Esto hace que la creación y el borrado sean una acción más natural.

  • POST vs. GET para sesiones es fácil. Si envía usuario / contraseña como variables, usaría POST porque no quiero que se envíe la contraseña como parte del URI. Aparecerá en registros y posiblemente quede expuesto sobre el cable. También corre el riesgo de que el software falle en las limitaciones de GET args.

  • Por lo general, uso la autenticación básica o ninguna autenticación con los servicios REST.

Creando usuarios

  • Es un recurso, por lo que no debería necesitar / registrarse.

    • POST / usuario: crea un usuario si el solicitante no puede especificar la identificación
    • PUT / user / xxx: cree o actualice un usuario asumiendo que conoce la identificación de antemano
    • GET / usuario: enumera x ID de usuario
    • GET / user / xxx: obtiene los detalles del usuario con id xxx
    • DELETE / user / xxx - Elimina el usuario con id xxx
  • Qué tipo de identificación usar es una pregunta difícil. Tienes que pensar en hacer cumplir la singularidad, en la reutilización de identificadores antiguos que fueron ELIMINADOS. Por ejemplo, no desea utilizar esos identificadores como claves externas en un backend si los identificadores se van a reciclar (si es posible). Sin embargo, puede tener una búsqueda para la conversión de identificación externa / interna para mitigar los requisitos de backend.

dietbuddha
fuente
6
Esta es la mejor respuesta. / login y / logout no son recursos y rompen la idea de REST.
wle8300
5
Autenticación! = Sesión
dietbuddha
1
Sí, la tesis de Fielding establece en la sección 5.1.3 que "el estado de la sesión se [...] mantiene completamente en el cliente". Además, diría que, idealmente, la autenticación también debería ser sin estado en el lado del servidor, es decir, en lugar de almacenar "tickets de autenticación" activos en una base de datos, el servidor debería poder verificar una credencial de autenticación basándose únicamente en la credencial en sí, por ejemplo, utilizando un token criptográfico autónomo junto con una clave privada. Entonces, en lugar de un recurso / session, uno podría introducir un recurso / authentication, pero tampoco resuelve el problema ...
raner
En realidad, / login y / logout son sustantivos. Supongo que estás pensando en / log_in y / log_out.
TiggerToo
21

Simplemente hablaré de mi experiencia en la integración de varios servicios web REST para mis clientes, ya sea que se utilicen para aplicaciones móviles o para la comunicación de servidor a servidor, así como para la creación de API REST para otros. Aquí hay algunas observaciones que he recopilado de la API REST de otras personas, así como las que construimos nosotros mismos:

  • Cuando decimos API, normalmente se refiere al conjunto de interfaz de programación y no necesariamente a la capa de presentación. REST también se centra en los datos y no en la presentación. Dicho esto, la mayoría de REST devuelve datos en forma de JSON o XML y rara vez devuelve una capa de presentación específica. Este rasgo (de devolver datos y no la página web directa) le dio a REST la capacidad de realizar la entrega de múltiples canales. Lo que significa que el mismo servicio web se puede renderizar en HTML, iOS, Android o incluso usarse como combinación de servidor a servidor.
  • Es muy raro combinar HTML y REST como URL. Por defecto, REST son pensamientos como servicios y no tienen capa de presentación. Es el trabajo de quienes consumen los servicios web renderizar los datos de los servicios que llaman de acuerdo a lo que quieren. Hasta ese punto, su URL a continuación no se ajusta a la mayoría de los diseños basados ​​en REST que he encontrado hasta ahora (ni a los estándares, como los que provienen de Facebook o Twitter).
GET / register // obtiene la página web que tiene el formulario de registro
  • Continuando con el punto anterior, también es poco común (y no lo he encontrado) que el servicio basado en REST realice redirecciones como las que se sugieren a continuación:
GET / logout // destruye la sesión y redirige a /
POST / login // autentica las credenciales contra la base de datos y redirige a casa con una nueva sesión o redirige de nuevo a / login
 

Como REST están diseñados como servicios, funciones como el inicio de sesión y el cierre de sesión normalmente devuelven un resultado de éxito / error (normalmente en formato de datos JSON o XML) que luego el consumidor interpretaría. Dicha interpretación podría incluir la redirección a la página web adecuada como mencionó.

  • En REST, la URL significa las acciones que se realizan. Por esa razón, debemos eliminar la mayor ambigüedad posible. Si bien es legítimo en su caso tener GET y POST que tengan la misma ruta (como / register) que realicen una acción diferente, dicho diseño introduce ambigüedad en los servicios prestados y puede confundir al consumidor de sus servicios. Por ejemplo, las URL como la que presenta a continuación no son ideales para servicios basados ​​en REST
GET / register // obtiene la página web que tiene el formulario de registro
POST / register // registra la información ingresada en la base de datos como un nuevo / usuario / xxx

Estos son algunos puntos de lo que he tratado. Espero que pueda proporcionarle algunas ideas.

Ahora, en cuanto a la implementación de su REST, estas son las implementaciones típicas que he encontrado:

  • OBTENER / cerrar sesión  
    

    Ejecute el cierre de sesión en el backend y devuelva JSON para indicar el éxito / fracaso de la operación

  • POST / iniciar sesión
    

    Envíe las credenciales al backend. Devuelve éxito / fracaso. Si tiene éxito, normalmente también devolverá el token de sesión, así como la información del perfil.

  • POST / registrarse
    

    Envíe el registro al backend. Devuelve éxito / fracaso. Si tiene éxito, normalmente se trata de la misma manera que un inicio de sesión exitoso o puede optar por registrarse como un servicio distinto

  • OBTENER / usuario / xxx
    

    Obtenga el perfil de usuario y devuelva el formato de datos JSON para el perfil del usuario

  • POST / usuario / xxx 
    // renombrado a 
    POST / updateUser / xxx
    

    Publique información de perfil actualizada en formato JSON y actualice la información en el backend. Devolver éxito / fracaso a la persona que llama

momo
fuente
3
Sí, si está integrando su API REST con una aplicación basada en HTML (a través de Javascript y AJAX), obtendrá enormes beneficios ya que JSON es analizado de forma nativa por JavaScript. En Android / Java, JSON es más fácil y sencillo de analizar en comparación con XML.
momo
15
GET / logout es peligroso. GET debería ser idempotente. ¡También a los navegadores les gusta precargar <a> hrefs, que lo cerrarán!
Kugel
4

Creo que este es un enfoque RESTful para la autenticación. Para iniciar sesión, utiliza HttpPut. Este método HTTP se puede utilizar para la creación cuando se proporciona la clave y las llamadas repetidas son idempotentes. Para LogOff, especifique la misma ruta en el HttpDeletemétodo. No se utilizan verbos. Pluralización adecuada de la colección. Los métodos HTTP apoyan el propósito.

[HttpPut]
[Route("sessions/current")]
public IActionResult LogIn(LogInModel model) { ... }

[HttpDelete]
[Route("sessions/current")]
public IActionResult LogOff() { ... }

Si lo desea, puede sustituir la corriente por activa.

Chad Kuehn
fuente
1

Recomendaría usar una URL de cuenta de usuario similar a Twitter, donde la URL de la cuenta de usuario sería algo foo.com/myUserNameasí como puede acceder a mi cuenta de Twitter con la URL https://twitter.com/joelbyler

No estoy de acuerdo con que el cierre de sesión requiera un POST. Como parte de su API, si va a mantener una sesión, entonces una identificación de sesión en forma de UUID podría ser algo que se puede usar para realizar un seguimiento de un usuario y confirmar que la acción que se está tomando está autorizada. Entonces, incluso un GET puede pasar la identificación de la sesión al recurso.

En resumen, recomendaría que lo mantenga simple, las URL deben ser cortas y memorables.

joelbyler
fuente
La pregunta es sobre los recursos de la API. Tu respuesta es sobre la capa de presentación.
Henno