suspensivo remedios recursos recurso modelo materia los jerarquico efectos efecto derecho administrativos administrativo administrativa rest design

remedios - Actualizar/crear recursos jerárquicos REST



recursos en materia administrativa (4)

En realidad, desde el paso exitoso (1) debe obtener el código HTTP 201 Creado y una dirección (URL) al recurso recién creado, no solo el número de ID. Si el paso (2) falla, su API REST debe indicar si el problema es con el cliente, como un documento mal formado (código de problema 4xx) o servidor (5xx). Por ejemplo, si mientras tanto se borró el recurso 42, se debe devolver el código 404 no encontrado.

Ahí radica el problema con las API REST sin estado: no pueden admitir transacciones compuestas por más de una solicitud. Para que esto sea posible, deberá mantener una sesión (estado) en el servidor.

Por cierto, la URL en el paso (3) en su ejemplo sugiere que está sustituyendo a todos los usuarios y probablemente debería leer http://api.example.com/users/42 .

Puede elegir entre enviar un documento completo de usuario + perfil a la vez para dividirlo en dos registros de base de datos en una transacción atómica o para permitir la persistencia de datos de usuario parciales, es decir, usuario sin perfil.

La elección depende del contexto. Por ejemplo, puede estar perfectamente bien que un usuario no tenga un perfil (para que pueda ser proporcionado por el usuario). Por el contrario, tener un registro de perfil, que no pertenece a ningún usuario, probablemente no sea aceptable. La discusión sobre la aplicación de esta lógica va más allá del alcance de su pregunta y variará según el tipo de tienda persistente (base de datos) que elija. Las bases de datos relacionales hacen cumplir esto usando claves foráneas.

Estoy trabajando en una REST API y estoy tratando de entender cómo tratar con los recursos jerárquicos.

Fondo

Comencemos con un ejemplo simple. En mi API tengo usuarios , perfiles de usuario y reseñas .

  • Los usuarios deben tener un perfil de usuario asociado (un perfil de usuario corresponde a un solo usuario)
  • Los usuarios pueden tener una revisión asociada (una revisión corresponde a un solo usuario)

La representación de recursos del usuario debe ser:

User: { "u1": "u1value", // User''s attributes "u2": "u2value", ... "links": [{ "rel": "profile", "href": "http://..." // URI of the profile resource }, { "rel": "review", "href": "http://..." // URI of the review resource }] }

La representación de recursos del perfil de usuario debe ser:

UserProfile: { "p1": "p1value", // Profile attributes "p2": "p2value", ... "links": [{ "rel": "owner", "href": "http://..." // URI of the user resource }] }

La representación del recurso de revisión debe ser:

Review: { "r1": "r1value", // Review attributes "r2": "r2value", ... "links": [{ "rel": "owner", "href": "http://..." // URI of the user resource }] }

Los URI de recursos podrían ser:

  • http://api.example.com/users/{userid} : acceso al recurso del usuario
  • http://api.example.com/users/{userid}/profile : acceso al recurso de perfil del usuario
  • http://api.example.com/users/{userid}/review : acceso al recurso de revisión del usuario

Creación de recursos: ¿cuál es la forma correcta de crear un usuario?

Ahora quiero crear un nuevo usuario:

  1. POST http://api.example.com/users {"u1": "bar", "u2": "foo"} y obtengo el nuevo ID de usuario = 42
  2. POST http://api.example.com/users/42/profile {"p1": "baz", "p2": "asd"}
  3. PUT http://api.example.com/users {"u1": "bar", "u2": "foo", links: [{"rel": "profile", "href": "http://api.example.com/users/42/profile"]}

Mis preocupaciones:

  • ¿Qué pasa si algo se rompe entre 1 y 2 o 2 y 3?
  • En 3), ¿debería el servidor actualizar automágicamente los enlaces en el http://api.example.com/users/42/profile , para señalar al propietario correcto?
  • ¿Actualizar los campos de enlace es la forma correcta de crear relaciones? ¿O debería omitir el paso 3) y dejar que el sistema adivine las relaciones de acuerdo con las convenciones de URI? (Leí en varios libros que el URI debería considerarse opaco).

Creo que tus llamadas deberían ser así

1) Crea un usuario

POST http://api.example.com/users + params in payload

Si devuelve HTTP 201 + ID de usuario, puede continuar y crear el perfil. De lo contrario, tratará la excepción de la forma que desee. Debería esperar a la primera llamada para volver antes de comenzar la creación del perfil.

2) Crear un perfil asociado a un usuario 42 (si la creación del usuario fue correcta)

POST http://api.example.com/users/42/profile + params in payload

devuelve HTTP 201 + identificación de perfil

Su backend será responsable de actualizar su objeto de usuario y el objeto de perfil (y su base de datos) para que los usuarios 42 se vincularán al nuevo perfil. Si el servidor no puede vincular el objeto, puede enviar un error de 500 explicando lo sucedido.

Entonces, en mi opinión, me saltaría el paso 3.

Ahora entiendo que su punto es sobre el hecho de que un usuario DEBE tener un perfil

Veo 2 soluciones

1) podría crear un perfil asociado vacío al crear su usuario. Luego, al consultar al usuario puede obtener el ID de perfil y modificarlo con un PUT (Realmente no me gusta esta solución porque cuando le pide a su API que cree un usuario, solo debe crear un usuario y nada más que de hecho una el perfil es obligatorio, no tan feo como parece).

2) podría tener una propiedad en su usuario que diga si el usuario es correcto o no (lo que significa que tiene un perfil asociado). Al final del proceso de creación, si su usuario 42 no es correcto, puede eliminarlo o volver a intentar la creación del perfil ... Entonces podría consultar solo usuarios correctos con algo como / users? IsCorrect = true

3) Permita que el cliente trate el hecho de que un usuario como no perfil -> muestre una ventana emergente para solicitar la creación del perfil ...

Eche un vistazo a este documento para conocer las mejores prácticas de REST API

Tal vez también podrías echarle un vistazo a HAL, que intenta lidiar con la relación entre los objetos.

Y por último, pero no por ello menos importante, podrías seguir el grupo de google de api-craft, donde encontrarás temas interesantes relacionados con tu problema.


Debe apegarse a HATEOAS y eliminar la referencia de las URL que obtiene en sus respuestas:

Para facilitar el acceso, digamos User.profile contiene el href del link con rel == profile .

Crear el usuario

Con el POST que describió ... pero no debería devolver una identificación, sino un usuario, completo con sus enlaces.

User: { "u1": "bar", // User''s attributes "u2": "foo", ... "profile": "http://api.example.com/users/42/profile", "links": [{ "rel": "profile", "href": "http://api.example.com/users/42/profile" }, ... ] }

En este punto, el recurso de perfil en User.profile (podría ser http://api.example.com/users/42/profile , o la ubicación a la que migre en el futuro) es cualquiera que sea el perfil predeterminado, por ejemplo, un documento vacío o solo con el enlace del propietario lleno.

Actualiza el perfil

profile = GET User.profile profile.p1 = "baz" profile.p2 = "asd" PUT profile to the same url you just dereferenced

Al desreferenciar hrefs en sus documentos en lugar de construir URL con identificadores que obtiene de las respuestas, su cliente no tendrá que cambiar cuando cambie la API. Al igual que cuando: los perfiles se mueven a http://profiles.cdn.example.com/ - los perfiles obtienen un valor de p3

Los clientes API "antiguos" seguirán trabajando sin tener que cambiar ningún código.


Sus preocupaciones están bien ubicadas y su lista de problemas es correcta. Si puedo sugerir, su enfoque se parece mucho a que está utilizando un enfoque de DB relacional y está haciendo un INSERT, recuperando el PK de una secuencia que usa para el siguiente INSERT, y así sucesivamente.

Deje que el servidor mantenga la integridad referencial

Como observación, incluso si sigue su esquema original, omita el paso 3 por completo . El URI en los links que es visible cuando recupera su documento de usuario debe ser generado por el servidor en función de la existencia del registro de perfil.

Por ejemplo, si utiliza un back-end relacional, SELECCIONE de USUARIOS para obtener el registro del usuario. A continuación, SELECCIONE de PERFILES. Si hay un registro, entonces modifica la estructura de datos de retorno para incluir la referencia.

PUBLICAR documentos enteros

Una forma común de resolver los otros problemas que presenta es permitir la publicación de un documento completo en la URL del usuario (como bases de datos NoSQL como MongoDB ). Aquí el documento es el usuario y el perfil:

{ "u1": "bar", "u2": "foo", "profile": { "p1": "baz", "p2": "asd" } }

En este enfoque, su punto final en el servidor recibe una estructura anidada (documento) y realiza el INSERTO en USUARIOS, recupera el PK y luego realiza el INSERTO en PERFILES utilizando este PK. Hacer esto en el servidor resuelve varias preocupaciones:

  1. La transacción puede ser atómica
  2. Solo hay un intercambio de red entre el cliente y el servidor
  3. El servidor hace el trabajo pesado y puede validar toda la transacción (por ejemplo, el usuario no se crea si el perfil no es válido)
  4. No es necesario el paso 3.

Tenga en cuenta que este enfoque se suma a las API que ha detallado anteriormente; aún desea poder acceder directamente al perfil de un usuario.

GET - el cliente puede especificar campos

Es interesante comparar con las API de empresas bien establecidas. Toma LinkedIn por ejemplo. En su API de desarrollador, el GET predeterminado para un usuario devuelve simplemente el nombre del usuario, título y URI.

Sin embargo, si la solicitud especifica campos adicionales, puede obtener los datos anidados, por ejemplo, el segundo ejemplo en http://developer.linkedin.com/documents/understanding-field-selectors devuelve el nombre del usuario y una lista de nombres de compañías para las posiciones ellos han sostenido. Podría implementar un esquema similar para Perfiles y Revisiones.

PATCH para actualizar las propiedades del documento

Con la inserción y consultas fuera del camino, podría valer la pena considerar cómo actualizar los datos (PATCH). Sobrescribir un campo es obvio, por lo que podría, por ejemplo, PATCH a http://api.example.com/users/42 lo siguiente:

{ "u1": null, "u2": "new-foo", "profile": { "p1": "new-baz"} }

Lo cual desarmaría u1 , establecería u2 en new-foo y actualizaría la p1 del perfil a new-baz . Tenga en cuenta que si falta un campo ( p2 ), entonces el campo no se modifica. PATCH es preferible a PUT anterior, como se explica en esta respuesta .

Si solo necesita actualizar el perfil, PATCH el nuevo registro de perfil directamente a http://api.example.com/users/42/profile

DELETE debería en cascada

Por último, la eliminación se puede hacer con el método DELETE apuntando al recurso que desea eliminar, ya sea Usuario, Perfil o Revisión. Implemente una eliminación en cascada para que eliminar un usuario elimine su perfil y revisiones.