Supongamos que una API REST, en respuesta a una GET
solicitud HTTP , devuelve algunos datos adicionales en un subobjeto owner
:
{
id: 'xyz',
... some other data ...
owner: {
name: 'Jo Bloggs',
role: 'Programmer'
}
}
Claramente, no queremos que nadie pueda PUT
respaldar
{
id: 'xyz',
... some other data ...
owner: {
name: 'Jo Bloggs',
role: 'CEO'
}
}
y que tenga éxito. De hecho, probablemente ni siquiera vamos a implementar una forma para que eso tenga éxito, en este caso.
Pero esta pregunta no se trata solo de los subobjetos: ¿qué se debe hacer, en general, con los datos que no se pueden modificar en una solicitud PUT?
¿Debería ser necesario que falte en la solicitud PUT?
¿Debería descartarse en silencio?
¿Debería verificarse y, si difiere del valor anterior de ese atributo, devolver un código de error HTTP en la respuesta?
¿O deberíamos usar parches JSON RFC 6902 en lugar de enviar el JSON completo?
rest
http
http-request
Robin Green
fuente
fuente
Respuestas:
No existe una regla, ya sea en la especificación W3C o en las reglas no oficiales de REST, que diga que a
PUT
debe usar el mismo esquema / modelo que su correspondienteGET
.Es agradable si son similares , pero no es inusual
PUT
que las cosas se hagan de manera ligeramente diferente. Por ejemplo, he visto muchas API que incluyen algún tipo de ID en el contenido devuelto porGET
, por conveniencia. Pero con aPUT
, esa identificación está determinada exclusivamente por el URI y no tiene ningún significado en el contenido. Cualquier identificación encontrada en el cuerpo será ignorada en silencio.REST y la web en general están fuertemente vinculados al Principio de Robustez : "Sea conservador en lo que hace [enviar], sea liberal en lo que acepta". Si está de acuerdo filosóficamente con esto, entonces la solución es obvia: ignore los datos no válidos en las
PUT
solicitudes. Eso se aplica tanto a los datos inmutables, como en su ejemplo, como a las tonterías reales, por ejemplo, campos desconocidos.PATCH
es potencialmente otra opción, pero no debe implementar aPATCH
menos que realmente sea compatible con actualizaciones parciales.PATCH
significa solo actualizar los atributos específicos que incluyo en el contenido ; sí no significa reemplazar toda la entidad, pero excluye algunos campos específicos . De lo que realmente estás hablando no es realmente una actualización parcial, es una actualización completa, idempotente y todo, es solo que parte del recurso es de solo lectura.Una buena cosa si elige esta opción sería enviar un 200 (OK) con la entidad actualizada real en la respuesta, para que los clientes puedan ver claramente que los campos de solo lectura no se actualizaron.
Ciertamente, hay algunas personas que piensan lo contrario: que debería ser un error intentar actualizar una parte de solo lectura de un recurso. Hay alguna justificación para esto, principalmente sobre la base de que definitivamente devolvería un error si todo el recurso fuera de solo lectura y el usuario intentara actualizarlo. Definitivamente va en contra del principio de robustez, pero puede considerar que es más "autodocumentado" para los usuarios de su API.
Hay dos convenciones para esto, que corresponden a sus ideas originales, pero las ampliaré. El primero es prohibir que los campos de solo lectura aparezcan en el contenido y devolver un HTTP 400 (Solicitud incorrecta) si lo hacen. Las API de este tipo también deberían devolver un HTTP 400 si hay otros campos no reconocidos / inutilizables. El segundo es requerir que los campos de solo lectura sean idénticos al contenido actual y devolver un 409 (Conflicto) si los valores no coinciden.
Realmente no me gusta la verificación de igualdad con 409 porque invariablemente requiere que el cliente haga un
GET
para recuperar los datos actuales antes de poder hacer unPUT
. Eso no es bueno y probablemente conducirá a un bajo rendimiento, para alguien, en algún lugar. Tampoco me gusta 403 (Prohibido) para esto, ya que implica que todo el recurso está protegido, no solo una parte de él. Entonces, mi opinión es que si debe validar en lugar de seguir el principio de robustez, valide todas sus solicitudes y devuelva un 400 para cualquiera que tenga campos adicionales o no modificables.Asegúrese de que su 400/409 / lo que sea incluye información sobre cuál es el problema específico y cómo solucionarlo.
Ambos enfoques son válidos, pero prefiero el primero de acuerdo con el principio de robustez. Si alguna vez ha experimentado trabajar con una API REST grande, apreciará el valor de la compatibilidad con versiones anteriores. Si alguna vez decide eliminar un campo existente o hacerlo de solo lectura, es un cambio compatible con versiones anteriores si el servidor simplemente ignora esos campos y los clientes antiguos seguirán funcionando. Sin embargo, si realiza una validación estricta del contenido, ya no es compatible con versiones anteriores y los clientes antiguos dejarán de funcionar. El primero generalmente significa menos trabajo tanto para el mantenedor de una API como para sus clientes.
fuente
Idem potencia
Después del RFC, un PUT tendría que entregar un objeto completo al recurso. La razón principal de esto es que PUT debe ser idempotente. Esto significa que una solicitud, que se repite, debe evaluar el mismo resultado en el servidor.
Si permite actualizaciones parciales, ya no puede ser idem-potente. Si tienes dos clientes. Cliente A y B, entonces el siguiente escenario puede evolucionar:
El cliente A obtiene una imagen de las imágenes de recursos. Contiene una descripción de la imagen, que todavía es válida. El cliente B coloca una nueva imagen y actualiza la descripción en consecuencia. La imagen ha cambiado. El cliente A ve, no tiene que cambiar la descripción, porque es lo que desea y solo coloca la imagen.
Esto conducirá a una inconsistencia, ¡la imagen tiene los metadatos incorrectos adjuntos!
Aún más molesto es que cualquier intermediario puede repetir la solicitud. En caso de que decida de alguna manera, el PUT falló.
El significado de PUT no se puede cambiar (aunque puede usarlo incorrectamente).
Otras opciones
Afortunadamente hay otra opción, esta es PATCH. PATCH es un método que le permite actualizar parcialmente una estructura. Simplemente puede enviar una estructura parcial. Para aplicaciones simples, esto está bien. No se garantiza que este método sea ideal. El cliente debe enviar una solicitud en el siguiente formulario:
Y el servidor puede responder con 204 (Sin contenido) para marcar el éxito. En caso de error, no puede actualizar una parte de la estructura. El método PATCH es atómico.
La desventaja de este método es que no todos los navegadores lo admiten, pero esta es la opción más natural en un servicio REST.
Ejemplo de solicitud de parche: http://tools.ietf.org/html/rfc5789#section-2.1
Json parcheando
La opción json parece ser bastante completa y una opción interesante. Pero puede ser difícil de implementar para terceros. Tienes que decidir si tu base de usuarios puede manejar esto.
También es algo complicado, porque necesita construir un pequeño intérprete que convierta los comandos en una estructura parcial, que utilizará para actualizar su modelo. Este intérprete también debe verificar si los comandos provistos tienen sentido. Algunos comandos se cancelan entre sí. (escriba fielda, elimine fielda). Creo que desea informar esto al cliente para limitar el tiempo de depuración de su lado.
Pero si tienes tiempo, esta es una solución realmente elegante. Aún debe validar los campos, por supuesto. Puede combinar esto con el método PATCH para permanecer en el modelo REST. Pero creo que POST sería aceptable aquí.
Yendo mal
Si decide ir con la opción PUT, que es algo arriesgado. Entonces al menos no debes descartar el error. El usuario tiene una cierta expectativa (los datos se actualizarán) y si rompe esto, le dará a algunos desarrolladores un mal momento.
Podrías elegir para volver a marcar: 409 Conflict o 403 Forbidden Depende de cómo veas el proceso de actualización. Si lo ve como un conjunto de reglas (centradas en el sistema), el conflicto será mejor. Algo así, estos campos no son actualizables. (En conflicto con las reglas). Si lo ve como un problema de autorización (centrado en el usuario), debe regresar prohibido. Con: no tiene autorización para cambiar estos campos.
Todavía debe obligar a los usuarios a enviar todos los campos modificables.
Una opción razonable para hacer cumplir esto es establecerlo en un subrecurso, que solo ofrece los datos modificables.
Opinión personal
Personalmente, iría (si no tiene que trabajar con navegadores) para el modelo PATCH simple y luego lo extendería con un procesador de parches JSON. Esto se puede hacer diferenciando en los tipos mime: El tipo mime de parche json:
aplicación / parche json
Y json: application / json-patch
facilita la implementación en dos fases.
fuente