Comprensión de REST: verbos, códigos de error y autenticación

602

Estoy buscando una manera de ajustar las API alrededor de las funciones predeterminadas en mis aplicaciones web, bases de datos y CMS basados ​​en PHP.

Miré a mi alrededor y encontré varios marcos de "esqueleto". Además de las respuestas en mi pregunta, hay Tonic , un marco REST que me gusta porque es muy liviano.

Me gusta REST lo mejor por su simplicidad, y me gustaría crear una arquitectura API basada en ella. Estoy tratando de entender los principios básicos y aún no lo he entendido completamente. Por lo tanto, una serie de preguntas.

1. ¿Lo estoy entendiendo bien?

Digamos que tengo un recurso "usuarios". Podría configurar una serie de URI de esta manera:

/api/users     when called with GET, lists users
/api/users     when called with POST, creates user record
/api/users/1   when called with GET, shows user record
               when called with PUT, updates user record
               when called with DELETE, deletes user record

¿Es esta una representación correcta de una arquitectura RESTful hasta ahora?

2. Necesito más verbos.

Crear, actualizar y eliminar puede ser suficiente en teoría, pero en la práctica tendré la necesidad de muchos más verbos. Me doy cuenta de que estas son cosas que podrían integrarse en una solicitud de actualización, pero son acciones específicas que pueden tener códigos de retorno específicos y no me gustaría agruparlas en una sola acción.

Algunos que vienen a la mente en el ejemplo del usuario son:

activate_login
deactivate_login
change_password
add_credit

¿Cómo expresaría acciones como las de una arquitectura de URL RESTful?

Mi instinto sería hacer una llamada GET a una URL como

/api/users/1/activate_login 

y esperar un código de estado de vuelta.

Sin embargo, eso se desvía de la idea de usar verbos HTTP. ¿Qué piensas?

3. Cómo devolver mensajes de error y códigos

Una gran parte de la belleza de REST proviene del uso de métodos HTTP estándar. En caso de error, emito un encabezado con un código de estado de error 3xx, 4xx o 5xx. Para una descripción detallada del error, puedo usar el cuerpo (¿verdad?). Hasta aquí todo bien. Pero, ¿cuál sería la forma de transmitir un código de error propietario que sea más detallado al describir qué salió mal (por ejemplo, "no se pudo conectar a la base de datos" o "inicio de sesión incorrecto de la base de datos")? Si lo pongo en el cuerpo junto con el mensaje, tengo que analizarlo después. ¿Hay un encabezado estándar para este tipo de cosas?

4. Cómo hacer la autenticación

  • ¿Cómo sería una autenticación basada en la clave API siguiendo los principios REST?
  • ¿Existen puntos fuertes contra el uso de sesiones al autenticar un cliente REST, aparte de eso, es una violación flagrante del principio REST? :) (solo bromeo aquí, la autenticación basada en sesión funcionaría bien con mi infraestructura existente).
Pekka
fuente
13
@Daniel, gracias por la edición. "I more verbs" fue un juego de palabras intencional, pero lo dejo como está, es más fácil de leer ahora. :)
Pekka
1
Por cierto, sobre la descripción del error. Terminé poniendo la descripción del error en el encabezado de la respuesta. Simplemente agregue el encabezado llamado 'Descripción del error'.
Andrii Muzychuk
Esto se parece más a las preguntas de seguridad de la aplicación. La seguridad de la aplicación no es de lo que se trata REST.
Nazar Merza
@NazarMerza ¿cómo son las preguntas de seguridad de aplicaciones 1., 2. y 3.?
Pekka

Respuestas:

621

Noté esta pregunta un par de días tarde, pero siento que puedo agregar algunas ideas. Espero que esto pueda ser útil para su aventura RESTful.


Punto 1: ¿Lo estoy entendiendo bien?

Entendiste bien. Esa es una representación correcta de una arquitectura RESTful. Puede encontrar la siguiente matriz de Wikipedia muy útil para definir sus sustantivos y verbos:


Cuando se trata de un URI de colección como:http://example.com/resources/

  • OBTENER : enumere los miembros de la colección, completa con sus URI de miembros para una mayor navegación. Por ejemplo, enumere todos los autos en venta.

  • PUT : Significado definido como "reemplazar toda la colección con otra colección".

  • POST : cree una nueva entrada en la colección donde la identificación se asigna automáticamente por la colección. El ID creado generalmente se incluye como parte de los datos devueltos por esta operación.

  • ELIMINAR : Significado definido como "eliminar toda la colección".


Cuando se trata de un URI de miembro como:http://example.com/resources/7HOU57Y

  • GET : recupera una representación del miembro dirigido de la colección expresada en un tipo MIME apropiado.

  • PUT : actualice el miembro direccionado de la colección o créelo con la ID especificada.

  • POST : trata al miembro direccionado como una colección por derecho propio y crea un nuevo subordinado del mismo.

  • BORRAR : Eliminar el miembro dirigido de la colección.


Punto 2: necesito más verbos

En general, cuando cree que necesita más verbos, en realidad puede significar que sus recursos deben volver a identificarse. Recuerde que en REST siempre está actuando sobre un recurso o sobre una colección de recursos. Lo que elija como recurso es bastante importante para su definición de API.

Activar / Desactivar inicio de sesión : si está creando una nueva sesión, es posible que desee considerar "la sesión" como el recurso. Para crear una nueva sesión, use POST http://example.com/sessions/con las credenciales en el cuerpo. Para caducar, use PUT o DELETE (tal vez dependiendo de si tiene la intención de mantener un historial de sesión) para http://example.com/sessions/SESSION_ID.

Cambiar contraseña: esta vez el recurso es "el usuario". Necesitaría un PUT http://example.com/users/USER_IDcon las contraseñas antiguas y nuevas en el cuerpo. Está actuando sobre el recurso "el usuario" y una contraseña de cambio es simplemente una solicitud de actualización. Es bastante similar a la instrucción UPDATE en una base de datos relacional.

Mi instinto sería hacer una llamada GET a una URL como /api/users/1/activate_login

Esto va en contra de un principio REST muy importante: el uso correcto de los verbos HTTP. Cualquier solicitud GET nunca debe dejar ningún efecto secundario.

Por ejemplo, una solicitud GET nunca debe crear una sesión en la base de datos, devolver una cookie con una nueva ID de sesión o dejar ningún residuo en el servidor. El verbo GET es como la instrucción SELECT en un motor de base de datos. Recuerde que la respuesta a cualquier solicitud con el verbo GET debe poder almacenarse en caché cuando se solicita con los mismos parámetros, al igual que cuando solicita una página web estática.


Punto 3: Cómo devolver mensajes de error y códigos

Considere los códigos de estado HTTP 4xx o 5xx como categorías de error. Puedes elaborar el error en el cuerpo.

Error al conectarse a la base de datos: / Inicio de sesión incorrecto en la base de datos : en general, debe usar un error 500 para este tipo de errores. Este es un error del lado del servidor. El cliente no hizo nada malo. 500 errores normalmente se consideran "recuperables". es decir, el cliente puede volver a intentar la misma solicitud exacta y esperar que tenga éxito una vez que se resuelvan los problemas del servidor. Especifique los detalles en el cuerpo, para que el cliente pueda proporcionarnos algún contexto a los humanos.

La otra categoría de errores sería la familia 4xx, que en general indica que el cliente hizo algo mal. En particular, esta categoría de errores normalmente indica al cliente que no hay necesidad de volver a intentar la solicitud tal como es, porque continuará fallando permanentemente. es decir, el cliente necesita cambiar algo antes de volver a intentar esta solicitud. Por ejemplo, los errores "Recurso no encontrado" (HTTP 404) o "Solicitud con formato incorrecto" (HTTP 400) entrarían en esta categoría.


Punto 4: Cómo hacer la autenticación

Como se señaló en el punto 1, en lugar de autenticar a un usuario, es posible que desee pensar en crear una sesión. Se le devolverá una nueva "ID de sesión", junto con el código de estado HTTP apropiado (200: acceso concedido o 403: acceso denegado).

Luego le preguntará a su servidor RESTful: "¿Puede OBTENERME el recurso para esta ID de sesión?".

No hay modo autenticado: REST no tiene estado: crea una sesión, le pide al servidor que le proporcione recursos utilizando este ID de sesión como parámetro y, al cerrar sesión, abandona o caduca la sesión.

Daniel Vassallo
fuente
66
Muy bien, sin embargo, su uso de PUTcambiar una contraseña es probablemente incorrecto; PUTrequiere todo el recurso, por lo que tendría que enviar todos los atributos de usuario para cumplir con HTTP (y, por lo tanto, con HATEOAS REST). En cambio, para cambiar la contraseña simplemente se debe usar PATCHo POST.
Lawrence Dol
1
Creo que esta publicación sería perfecta si expandiera más sobre "POST: trata al miembro dirigido como una colección por derecho propio y crea un nuevo subordinado". medio. - Encontré lo que significa buscar en Google: es una excepción a su excelente respuesta.
Martin Konecny
66
No estoy de acuerdo con la última oración. Estás explicando cómo REST no tiene estado. Iniciar sesión para crear una sesión, luego cerrar sesión para finalizar la sesión después de hacer un trabajo es el mejor ejemplo de una API con estado.
Brandon
1
"Esto va en contra de un principio REST muy importante: el uso correcto de los verbos HTTP. Cualquier solicitud GET nunca debería dejar ningún efecto secundario". - ¿Qué sucede si desea mantener un recuento de aciertos para el recurso?
bobbyalex
1
Este artículo debería responder sus preguntas. saipraveenblog.wordpress.com/2014/09/29/rest-api-best-practices
java_geek
79

En pocas palabras, estás haciendo esto completamente al revés.

No debería enfocarse en esto desde las URL que debería usar. Las URL serán efectivamente "gratis" una vez que haya decidido qué recursos son necesarios para su sistema Y cómo representará esos recursos, y las interacciones entre los recursos y el estado de la aplicación.

Para citar a Roy Fielding

Una API REST debería dedicar casi todo su esfuerzo descriptivo a definir los tipos de medios utilizados para representar los recursos y dirigir el estado de la aplicación, o para definir nombres de relaciones extendidas y / o marcado habilitado para hipertexto para los tipos de medios estándar existentes. Cualquier esfuerzo dedicado a describir qué métodos usar en qué URI de interés debería definirse por completo dentro del alcance de las reglas de procesamiento para un tipo de medio (y, en la mayoría de los casos, ya definido por los tipos de medios existentes). [El fracaso aquí implica que la información fuera de banda está impulsando la interacción en lugar del hipertexto].

Las personas siempre comienzan con los URI y piensan que esta es la solución, y luego tienden a perder un concepto clave en la arquitectura REST, en particular, como se citó anteriormente, "La falla aquí implica que la información fuera de banda impulsa la interacción en lugar del hipertexto". "

Para ser honesto, muchos ven un montón de URI y algunos GET y PUT y POST y piensan que REST es fácil. RESTO no es fácil. RPC sobre HTTP es fácil, mover bloques de datos de un lado a otro a través de cargas HTTP es fácil. REST, sin embargo, va más allá de eso. REST es un protocolo agnóstico. HTTP es muy popular y apto para sistemas REST.

REST vive en los tipos de medios, sus definiciones y cómo la aplicación impulsa las acciones disponibles para esos recursos a través del hipertexto (enlaces, efectivamente).

Hay diferentes puntos de vista sobre los tipos de medios en los sistemas REST. Algunos prefieren cargas útiles específicas de la aplicación, mientras que otros prefieren elevar los tipos de medios existentes a roles apropiados para la aplicación. Por ejemplo, por un lado, tiene esquemas XML específicos diseñados para su aplicación en lugar de usar algo como XHTML como su representación, tal vez a través de microformatos y otros mecanismos.

Creo que ambos enfoques tienen su lugar, el XHTML funciona muy bien en escenarios que se superponen tanto con la web impulsada por humanos como por la máquina, mientras que los primeros tipos de datos más específicos que creo que facilitan las interacciones de máquina a máquina. Creo que la mejora de los formatos de los productos básicos puede dificultar la negociación de contenido. "application / xml + yourresource" es mucho más específico como tipo de medio que "application / xhtml + xml", ya que este último puede aplicarse a muchas cargas útiles que pueden o no ser algo en lo que un cliente de máquina está realmente interesado, ni tampoco determinar sin introspección.

Sin embargo, XHTML funciona muy bien (obviamente) en la web humana donde los navegadores web y la representación son muy importantes.

Su aplicación lo guiará en ese tipo de decisiones.

Parte del proceso de diseño de un sistema REST es descubrir los recursos de primera clase en su sistema, junto con los recursos de soporte derivados, necesarios para soportar las operaciones en los recursos primarios. Una vez que se descubren los recursos, la representación de esos recursos, así como los diagramas de estado que muestran el flujo de recursos a través del hipertexto dentro de las representaciones como el próximo desafío.

Recuerde que cada representación de un recurso, en un sistema de hipertexto, combina la representación real del recurso junto con las transiciones de estado disponibles para el recurso. Considere cada recurso como un nodo en un gráfico, siendo los enlaces las líneas que salen de ese nodo a otros estados. Estos enlaces informan a los clientes no solo sobre lo que se puede hacer, sino también sobre lo que se requiere para ellos (ya que un buen enlace combina el URI y el tipo de medio requerido).

Por ejemplo, puede tener:

<link href="http://example.com/users" rel="users" type="application/xml+usercollection"/>
<link href="http://example.com/users?search" rel="search" type="application/xml+usersearchcriteria"/>

Su documentación hablará sobre el campo rel denominado "usuarios" y el tipo de medio de "aplicación / xml + su usuario".

Estos enlaces pueden parecer redundantes, todos están hablando con el mismo URI, más o menos. Pero no lo son.

Esto se debe a que para la relación "usuarios", ese enlace está hablando de la colección de usuarios, y puede usar la interfaz uniforme para trabajar con la colección (GET para recuperarlos a todos, DELETE para borrarlos a todos, etc.)

Si PUBLICA en esta URL, deberá pasar un documento "application / xml + usercollection", que probablemente solo contendrá una única instancia de usuario dentro del documento para que pueda agregar el usuario, o no, quizás, para agregar varios en una vez. Quizás su documentación sugiera que simplemente puede pasar un solo tipo de usuario, en lugar de la colección.

Puede ver lo que requiere la aplicación para realizar una búsqueda, tal como se define en el enlace "buscar" y su tipo de medio. La documentación del tipo de medio de búsqueda le indicará cómo se comporta esto y qué esperar como resultados.

La conclusión aquí, sin embargo, es que los URI en sí mismos son básicamente poco importantes. La aplicación controla los URI, no los clientes. Más allá de unos pocos "puntos de entrada", sus clientes deben confiar en los URI proporcionados por la aplicación para su trabajo.

El cliente necesita saber cómo manipular e interpretar los tipos de medios, pero no necesita preocuparse por dónde va.

Estos dos enlaces son semánticamente idénticos a los ojos de los clientes:

<link href="http://example.com/users?search" rel="search" type="application/xml+usersearchcriteria"/>
<link href="http://example.com/AW163FH87SGV" rel="search" type="application/xml+usersearchcriteria"/>

Entonces, concéntrate en tus recursos. Concéntrese en sus transiciones de estado en la aplicación y cómo se logra mejor.

Will Hartung
fuente
1
Gracias Will por esta respuesta tan profunda. Varios puntos tomados. Me doy cuenta de que planificar desde "cómo se ve la URL" lo está haciendo al revés, y también estoy planeando desde el lado de los recursos. Tener URL para jugar solo me hace más fácil entender el concepto. Se podría ser que mis necesidades se pueden satisfacer con un sistema que no 100% de seguimiento principios REST como se define aquí. Dibujaré una lista completa de requisitos para cada tipo de recurso, creo que podré decidir entonces. Salud.
Pekka
30

re 1 : Esto se ve bien hasta ahora. Recuerde devolver el URI del usuario recién creado en un encabezado "Ubicación:" como parte de la respuesta a POST, junto con un código de estado "201 Creado".

re 2: La activación a través de GET es una mala idea, e incluir el verbo en el URI es un olor de diseño. Es posible que desee considerar devolver un formulario en un GET. En una aplicación web, este sería un formulario HTML con un botón de envío; en el caso de uso de la API, es posible que desee devolver una representación que contenga un URI a PUT para activar la cuenta. Por supuesto, también puede incluir este URI en la respuesta de POST a / users. El uso de PUT garantizará que su solicitud sea idempotente, es decir, se puede enviar de nuevo de forma segura si el cliente no está seguro del éxito. En general, piense en qué recursos puede convertir sus verbos (una especie de "nounificación de verbos"). Pregúntese con qué método está más estrechamente alineada su acción específica. Por ejemplo, change_password -> PUT; desactivar -> probablemente ELIMINAR; add_credit -> posiblemente POST o PUT.

3. No invente nuevos códigos de estado, a menos que crea que son tan genéricos que merecen ser estandarizados a nivel mundial. Intente utilizar el código de estado más apropiado disponible (lea sobre todos ellos en RFC 2616). Incluya información adicional en el cuerpo de respuesta. Si realmente está seguro de querer inventar un nuevo código de estado, piense de nuevo; si aún así lo cree, asegúrese de elegir al menos la categoría correcta (1xx -> OK, 2xx -> informativo, 3xx -> redirección; 4xx-> error del cliente, 5xx -> error del servidor). ¿Mencioné que inventar nuevos códigos de estado es una mala idea?

re 4. Si es posible, use el marco de autenticación integrado en HTTP. Vea cómo Google autentica en GData. En general, no coloque claves API en sus URI. Intente evitar sesiones para mejorar la escalabilidad y admitir el almacenamiento en caché: si la respuesta a una solicitud difiere debido a algo que sucedió antes, generalmente se ha vinculado a una instancia de proceso de servidor específica. Es mucho mejor convertir el estado de la sesión en un estado de cliente (por ejemplo, hacerlo parte de solicitudes posteriores) o hacerlo explícito convirtiéndolo en estado de recurso (servidor), es decir, darle su propio URI.

Stefan Tilkov
fuente
¿Puedes discutir por qué no poner claves API en las URL? ¿Es porque son visibles en los registros de proxy? ¿Qué pasa si las teclas son transitorias, basadas en el tiempo? ¿Qué pasa si se usa HTTPS?
MikeSchinkel
44
Además de violar el espíritu (los URI deben identificar las cosas), la consecuencia principal es que arruina el almacenamiento en caché.
Stefan Tilkov
22

1. Tienes la idea correcta sobre cómo diseñar tus recursos, en mi humilde opinión. No cambiaría nada.

2. En lugar de tratar de extender HTTP con más verbos, considere a qué se pueden reducir los verbos propuestos en términos de los métodos y recursos básicos de HTTP. Por ejemplo, en lugar de un activate_loginverbo, puede configurar recursos como: /api/users/1/login/activeque es un booleano simple. Para activar un inicio de sesión, solo hay PUTun documento que diga 'verdadero' o 1 o lo que sea. Para desactivar, hay PUTun documento allí que está vacío o dice 0 o falso.

Del mismo modo, cambiar o establecer contraseñas, acaba de hacer PUTs a /api/users/1/password.

Siempre que necesite agregar algo (como un crédito) piense en términos de POSTs. Por ejemplo, podría hacer un POSTa un recurso como /api/users/1/creditscon un cuerpo que contiene el número de créditos para agregar. A PUTen el mismo recurso podría usarse para sobrescribir el valor en lugar de agregar. A POSTcon un número negativo en el cuerpo se restaría, y así sucesivamente.

3. Recomiendo encarecidamente que no se extiendan los códigos de estado HTTP básicos. Si no puede encontrar uno que coincida exactamente con su situación, elija el más cercano y coloque los detalles del error en el cuerpo de la respuesta. Además, recuerde que los encabezados HTTP son extensibles; su aplicación puede definir todos los encabezados personalizados que desee. Una aplicación en la que trabajé, por ejemplo, podría devolver una 404 Not Founden múltiples circunstancias. En lugar de hacer que el cliente analice el cuerpo de respuesta por la razón, simplemente agregamos un nuevo encabezado X-Status-Extended, que contenía nuestras extensiones de código de estado patentadas. Entonces puede ver una respuesta como:

HTTP/1.1 404 Not Found    
X-Status-Extended: 404.3 More Specific Error Here

De esa manera, un cliente HTTP como un navegador web sabrá qué hacer con el código 404 normal, y un cliente HTTP más sofisticado puede elegir mirar el X-Status-Extendedencabezado para obtener información más específica.

4. Para la autenticación, recomiendo usar la autenticación HTTP si puede. Pero en mi humilde opinión, no hay nada de malo en usar la autenticación basada en cookies si eso es más fácil para usted.

friedo
fuente
44
Buena idea de usar recursos "extendidos" para hacer cosas a porciones más pequeñas de un recurso más grande.
womble
1
Las cookies son válidas en HTTP / REST, pero el servidor no debe almacenar la cookie como estado (por lo tanto, no como una sesión). Sin embargo, la cookie puede almacenar un valor como un HMAC, que se puede desmontar sin buscar el estado en otro lugar.
Bruce Alderson
14

Conceptos básicos de REST

REST tiene una restricción de interfaz uniforme, que establece que el cliente REST debe confiar en los estándares en lugar de los detalles específicos de la aplicación del servicio REST real, por lo que el cliente REST no se romperá con cambios menores, y probablemente será reutilizable.

Por lo tanto, existe un contrato entre el cliente REST y el servicio REST. Si usa HTTP como protocolo subyacente, los siguientes estándares son parte del contrato:

  • HTTP 1.1
    • definiciones de métodos
    • definiciones de código de estado
    • cabeceras de control de caché
    • aceptar y encabezados de tipo contenido
    • encabezados de autenticación
  • IRI (utf8 URI )
  • cuerpo (elige uno)
  • hipervínculos
    • qué debería contenerlos (elija uno)
      • enviando encabezados de enlace
      • enviando una respuesta hipermedia, por ejemplo, html, atom + xml, hal + json, ld + json e hydra, etc.
    • semántica
      • usar relaciones de enlace de IANA y probablemente relaciones de enlace personalizadas
      • usar un vocabulario RDF específico de la aplicación

REST tiene una restricción sin estado, que declara que la comunicación entre el servicio REST y el cliente debe ser sin estado. Esto significa que el servicio REST no puede mantener los estados del cliente, por lo que no puede tener un almacenamiento de sesión del lado del servidor. Tiene que autenticar cada solicitud individual. Entonces, por ejemplo, la autenticación básica HTTP (parte del estándar HTTP) está bien, ya que envía el nombre de usuario y la contraseña con cada solicitud.

Para contestar sus preguntas

  1. Si puede ser.

    Solo por mencionar, a los clientes no les importa la estructura IRI, les importa la semántica, porque siguen enlaces que tienen relaciones de enlace o atributos de datos vinculados (RDF).

    Lo único importante sobre los IRI es que un solo IRI debe identificar un solo recurso. Se permite que un solo recurso, como un usuario, tenga muchos IRI diferentes.

    Es bastante simple por qué usamos buenos IRI como /users/123/password; es mucho más fácil escribir la lógica de enrutamiento en el servidor cuando comprende el IRI simplemente al leerlo.

  2. Tiene más verbos, como PUT, PATCH, OPTIONS e incluso más, pero no necesita más ... En lugar de agregar nuevos verbos, debe aprender a agregar nuevos recursos.

    activate_login -> PUT /login/active true deactivate_login -> PUT /login/active false change_password -> PUT /user/xy/password "newpass" add_credit -> POST /credit/raise {details: {}}

    (El inicio de sesión no tiene sentido desde la perspectiva REST, debido a la restricción sin estado).

  3. A sus usuarios no les importa por qué existe el problema. Quieren saber solo si hay éxito o error, y probablemente un mensaje de error que puedan entender, por ejemplo: "Lo siento, pero no pudimos guardar su publicación", etc.

    Los encabezados de estado HTTP son sus encabezados estándar. Todo lo demás debería estar en el cuerpo, creo. Un solo encabezado no es suficiente para describir, por ejemplo, mensajes de error multilingües detallados.

  4. La restricción sin estado (junto con la caché y las restricciones del sistema en capas) aseguran que el servicio escala bien. Seguramente no desea mantener millones de sesiones en el servidor, cuando puede hacer lo mismo en los clientes ...

    El cliente de terceros obtiene un token de acceso si el usuario le otorga acceso mediante el cliente principal. Después de eso, el cliente de terceros envía el token de acceso con cada solicitud. Hay soluciones más complicadas, por ejemplo, puede firmar cada solicitud, etc. Para más detalles, consulte el manual de OAuth.

Literatura relacionada

inf3rno
fuente
11

Para los ejemplos que dijiste, usaría lo siguiente:

active_login

POST /users/1/activation

deactivate_login

DELETE /users/1/activation

Cambia la contraseña

PUT /passwords (esto supone que el usuario está autenticado)

añadir crédito

POST /credits (esto supone que el usuario está autenticado)

Para los errores, devolvería el error en el cuerpo en el formato en el que recibió la solicitud, por lo que si recibe:

DELETE /users/1.xml

Enviaría la respuesta de vuelta en XML, lo mismo sería cierto para JSON, etc.

Para la autenticación, debe usar la autenticación http.

jonnii
fuente
1
No usaría createcomo parte del URI (recuerde que los URI deberían ser sustantivos, y los métodos HTTP deberían ser verbos que operen con esos sustantivos). En cambio, tendría un recurso como el /users/1/activecual puede ser un simple booleano, y puede ser establecido poniendo un 1 o 0 a ese recurso.
Friedo
Tienes razón, saqué el / create. Debería ser solo una publicación en el recurso singleton.
jonnii
3
Tampoco lo usaría activationen el URI, a menos que manipule y administre explícitamente un recurso con el nombre de /users/1/activation. ¿Qué hace un GET sobre eso? ¿Qué hace un PUT? Me parece seguro que estás verificando el URI. Además, en cuanto a la negociación de tipo de contenido, también es mejor dejarlo fuera del URI e insertarlo en encabezados, como Accept.
Cheeso
6
  1. Use la publicación cuando no sepa cómo se vería el nuevo URI del recurso (si crea un nuevo usuario, la aplicación asignaría al nuevo usuario su ID), PUT para actualizar o crear recursos que sepa cómo van a estar representados (ejemplo : PUT /myfiles/thisismynewfile.txt)
  2. devolver la descripción del error en el cuerpo del mensaje
  3. Puede usar la autenticación HTTP (si es suficiente) Los servicios web deben ser estados
Arjan
fuente
5

Sugeriría (como primer paso) que PUTsolo debe usarse para actualizar las entidades existentes. POSTdebe usarse para crear otros nuevos. es decir

/api/users     when called with PUT, creates user record

no me parece bien Sin embargo, el resto de su primera sección (re. Uso del verbo) parece lógico.

Brian Agnew
fuente
probablemente alguien pensó que esto no era realmente una respuesta a su pregunta
lubos hasko
66
Mi opinión sobre PUT versus POST para crear nuevas entidades es usar PUT cuando la persona que llama controla el nombre del recurso, por lo que puede PUT al recurso exacto y POST cuando la persona que llama controla el nuevo nombre del recurso (como en el ejemplo aquí).
SteveD
5

Detallado, pero copiado de la especificación del método HTTP 1.1 en http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html

9.3 OBTENER

El método GET significa recuperar cualquier información (en forma de entidad) identificada por el URI de solicitud. Si el URI de solicitud se refiere a un proceso de producción de datos, son los datos producidos los que se devolverán como la entidad en la respuesta y no el texto fuente del proceso, a menos que ese texto sea el resultado del proceso.

La semántica del método GET cambia a un "GET condicional" si el mensaje de solicitud incluye un campo de encabezado If-Modified-Since, If-Unmodified-Since, If-Match, If-None-Match o If-Range. Un método GET condicional solicita que la entidad se transfiera solo en las circunstancias descritas por los campos de encabezado condicional. El método GET condicional está destinado a reducir el uso innecesario de la red al permitir que las entidades en caché se actualicen sin requerir múltiples solicitudes o transferir datos que ya tiene el cliente.

La semántica del método GET cambia a "GET parcial" si el mensaje de solicitud incluye un campo de encabezado Range. Un GET parcial solicita que solo se transfiera parte de la entidad, como se describe en la sección 14.35. El método GET parcial está destinado a reducir el uso innecesario de la red al permitir que se completen entidades parcialmente recuperadas sin transferir los datos que ya tiene el cliente.

La respuesta a una solicitud GET se puede almacenar en caché si y solo si cumple con los requisitos de almacenamiento en caché HTTP descritos en la sección 13.

Consulte la sección 15.1.3 para ver las consideraciones de seguridad cuando se utiliza para formularios.

9.5 POST

El método POST se utiliza para solicitar que el servidor de origen acepte la entidad incluida en la solicitud como un nuevo subordinado del recurso identificado por el URI de solicitud en la línea de solicitud. POST está diseñado para permitir un método uniforme para cubrir las siguientes funciones:

  - Annotation of existing resources;
  - Posting a message to a bulletin board, newsgroup, mailing list,
    or similar group of articles;
  - Providing a block of data, such as the result of submitting a
    form, to a data-handling process;
  - Extending a database through an append operation.

La función real realizada por el método POST la determina el servidor y, por lo general, depende del URI de solicitud. La entidad publicada está subordinada a ese URI de la misma manera que un archivo está subordinado a un directorio que lo contiene, un artículo de noticias está subordinado a un grupo de noticias en el que está publicado o un registro está subordinado a una base de datos.

La acción realizada por el método POST podría no dar como resultado un recurso que pueda ser identificado por un URI. En este caso, 200 (OK) o 204 (Sin contenido) es el estado de respuesta apropiado, dependiendo de si la respuesta incluye o no una entidad que describa el resultado.

Si se ha creado un recurso en el servidor de origen, la respuesta DEBERÍA ser 201 (Creado) y contener una entidad que describa el estado de la solicitud y se refiera al nuevo recurso, y un encabezado de Ubicación (ver sección 14.30).

Las respuestas a este método no se pueden almacenar en caché, a menos que la respuesta incluya los campos de encabezado Cache-Control o Expires apropiados. Sin embargo, la respuesta 303 (Ver otros) se puede usar para indicar al agente de usuario que recupere un recurso almacenable en caché.

Las solicitudes POST DEBEN obedecer los requisitos de transmisión de mensajes establecidos en la sección 8.2.

Vea la sección 15.1.3 para consideraciones de seguridad.

9.6 PONER

El método PUT solicita que la entidad adjunta se almacene bajo el URI de solicitud proporcionado. Si el URI de solicitud se refiere a un recurso ya existente, la entidad adjunta DEBE considerarse como una versión modificada de la que reside en el servidor de origen. Si el URI de solicitud no apunta a un recurso existente y el agente de usuario solicitante puede definir ese URI como un nuevo recurso, el servidor de origen puede crear el recurso con ese URI. Si se crea un nuevo recurso, el servidor de origen DEBE informar al agente de usuario a través de la respuesta 201 (Creado). Si se modifica un recurso existente, DEBERÍAN enviarse los códigos de respuesta 200 (OK) o 204 (Sin contenido) para indicar la finalización exitosa de la solicitud. Si el recurso no se pudo crear o modificar con el URI de solicitud, DEBE darse una respuesta de error apropiada que refleje la naturaleza del problema. El destinatario de la entidad NO DEBE ignorar ningún encabezado de Contenido * (por ejemplo, Rango de contenido) que no comprenda o implemente y DEBE devolver una respuesta 501 (No implementada) en tales casos.

Si la solicitud pasa a través de un caché y el URI de solicitud identifica una o más entidades actualmente en caché, esas entradas DEBERÍAN tratarse como obsoletas. Las respuestas a este método no se pueden almacenar en caché.

La diferencia fundamental entre las solicitudes POST y PUT se refleja en el significado diferente del Request-URI. El URI en una solicitud POST identifica el recurso que manejará la entidad adjunta. Ese recurso podría ser un proceso de aceptación de datos, una puerta de enlace a algún otro protocolo o una entidad separada que acepte anotaciones. Por el contrario, el URI en una solicitud PUT identifica la entidad adjunta a la solicitud: el agente de usuario sabe a qué se destina el URI y el servidor NO DEBE intentar aplicar la solicitud a otro recurso. Si el servidor desea que la solicitud se aplique a un URI diferente,

DEBE enviar una respuesta 301 (movida permanentemente); el agente de usuario PUEDE tomar su propia decisión con respecto a redirigir o no la solicitud.

Un único recurso PUEDE ser identificado por muchos URI diferentes. Por ejemplo, un artículo puede tener un URI para identificar "la versión actual" que es independiente del URI que identifica cada versión en particular. En este caso, una solicitud PUT en un URI general puede dar lugar a que el servidor de origen defina otros URI.

HTTP / 1.1 no define cómo un método PUT afecta el estado de un servidor de origen.

Las solicitudes PUT DEBEN obedecer los requisitos de transmisión de mensajes establecidos en la sección 8.2.

A menos que se especifique lo contrario para un encabezado de entidad particular, los encabezados de entidad en la solicitud PUT DEBERÍAN aplicarse al recurso creado o modificado por el PUT.

9.7 BORRAR

El método DELETE solicita que el servidor de origen elimine el recurso identificado por el URI de solicitud. Este método PUEDE ser anulado por intervención humana (u otro medio) en el servidor de origen. No se puede garantizar al cliente que la operación se haya llevado a cabo, incluso si el código de estado devuelto por el servidor de origen indica que la acción se ha completado con éxito. Sin embargo, el servidor NO DEBE indicar éxito a menos que, en el momento en que se dé la respuesta, tenga la intención de eliminar el recurso o moverlo a una ubicación inaccesible.

Una respuesta exitosa DEBE ser 200 (OK) si la respuesta incluye una entidad que describe el estado, 202 (Aceptada) si la acción aún no se ha promulgado, o 204 (Sin contenido) si la acción se ha promulgado pero la respuesta no incluye una entidad.

Si la solicitud pasa a través de un caché y el URI de solicitud identifica una o más entidades actualmente en caché, esas entradas DEBERÍAN tratarse como obsoletas. Las respuestas a este método no se pueden almacenar en caché.

gahooa
fuente
2

Acerca de los códigos de retorno REST: está mal mezclar códigos de protocolo HTTP y resultados REST.

Sin embargo, vi muchas implementaciones mezclándolas, y muchos desarrolladores pueden no estar de acuerdo conmigo.

Los códigos de retorno HTTP están relacionados con el HTTP Requestmismo. Una llamada REST se realiza mediante una solicitud de protocolo de transferencia de hipertexto y funciona a un nivel inferior que el método REST invocado. REST es un concepto / enfoque, y su salida es un resultado comercial / lógico , mientras que el código de resultado HTTP es un transporte .

Por ejemplo, devolver "404 No encontrado" cuando llama a / users / es confuso, porque puede significar:

  • URI está mal (HTTP)
  • No se encontraron usuarios (REST)

"403 Prohibido / Acceso denegado" puede significar:

  • Se necesita un permiso especial. Los navegadores pueden manejarlo preguntando al usuario / contraseña. (HTTP)
  • Permisos de acceso incorrectos configurados en el servidor. (HTTP)
  • Debes estar autenticado (REST)

Y la lista puede continuar con '500 Server error "(un error de HTTP Apache / Nginx o un error de restricción comercial en REST) ​​u otros errores HTTP, etc.

Según el código, es difícil entender cuál fue la razón de la falla, una falla HTTP (transporte) o una falla REST (lógica).

Si la solicitud HTTP se realizó físicamente con éxito, siempre debe devolver el código 200, independientemente de si el registro se encuentra o no. Porque el recurso URI se encuentra y fue manejado por el servidor http. Sí, puede devolver un conjunto vacío. ¿Es posible recibir una página web vacía con 200 como resultado http, verdad?

En lugar de esto, puede devolver 200 códigos HTTP y simplemente un JSON con una matriz / objeto vacía, o usar un indicador de resultado / éxito bool para informar sobre el estado de la operación realizada.

Además, algunos proveedores de Internet pueden interceptar sus solicitudes y devolverle un código HTTP 404. Esto no significa que no se encuentren sus datos, pero es algo incorrecto a nivel de transporte.

De Wiki :

En julio de 2004, el proveedor de telecomunicaciones del Reino Unido, BT Group, implementó el sistema de bloqueo de contenido Cleanfeed, que devuelve un error 404 a cualquier solicitud de contenido identificado como potencialmente ilegal por Internet Watch Foundation. Otros ISP devuelven un error "prohibido" HTTP 403 en las mismas circunstancias. La práctica de emplear errores falsos 404 como un medio para ocultar la censura también se ha informado en Tailandia y Túnez. En Túnez, donde la censura era severa antes de la revolución de 2011, la gente se dio cuenta de la naturaleza de los falsos errores 404 y creó un personaje imaginario llamado "Ammar 404" que representa "el censor invisible".

Marcodor
fuente