¿Hay algún problema con la implementación de métodos HTTP personalizados?

34

Tenemos una URL en el siguiente formato

/ instance / {instanceType} / {instanceId}

Puede llamarlo con los métodos HTTP estándar: POST, GET, DELETE, PUT. Sin embargo, hay algunas acciones más que tomamos como "Guardar como borrador" o "Conservar"

Pensamos que podríamos usar métodos HTTP personalizados como: DRAFT, VALIDATE, CURATE

Creo que esto es aceptable ya que las normas dicen

"El conjunto de métodos comunes para HTTP / 1.1 se define a continuación. Aunque este conjunto se puede ampliar, no se puede suponer que métodos adicionales compartan la misma semántica para clientes y servidores extendidos por separado".

Y herramientas como WebDav crean algunas de sus propias extensiones.

¿Hay problemas que alguien haya tenido con métodos personalizados? Estoy pensando en servidores proxy y firewalls, pero cualquier otra área de interés es bienvenida. ¿Debo permanecer en el lado seguro y solo tener un parámetro de URL como action = validate | curate | draft?

Juan mendes
fuente
66
Como cito de nuevo en RFC 1925 : "En el diseño del protocolo, la perfección se ha alcanzado no cuando no hay nada más que agregar, sino cuando no hay nada más que quitar". - Si funciona, no hay razón para agregar a http.
44
No pasa nada, siempre y cuando te des cuenta de que ahora estás usando un protocolo personalizado y no HTTP.
user16764
10
@ user16764 "El conjunto de métodos comunes para HTTP / 1.1 se define a continuación. Aunque este conjunto se puede ampliar, no se puede suponer que métodos adicionales compartan la misma semántica para clientes y servidores extendidos por separado". w3.org/Protocols/rfc2616/rfc2616-sec9.html Por lo tanto, está permitido y sigue siendo HTTP
Juan Mendes
En mi opinión, no hay nada que agregar / eliminar de HTTP, ya que las definiciones de métodos establecen que el uso de métodos personalizados ya es aceptable dentro del alcance de HTTP / 1.1, pero no se puede esperar que comparta la misma semántica, por lo que supongo que los puntos de @MichaelT y Juan Mendes pueden estar un poco apaciguado
Prof83

Respuestas:

42

Una de las limitaciones fundamentales de HTTP y la característica de diseño central de REST es una interfaz uniforme proporcionada por (entre otras cosas) un conjunto pequeño y fijo de métodos que se aplican universalmente a todos los recursos. La restricción de interfaz uniforme tiene una serie de ventajas y desventajas. Cito de Fielding generosamente aquí.

Una interfaz uniforme:

  • es más simple
  • desacopla las implementaciones de los servicios que brindan.
  • permite una arquitectura en capas, que incluye elementos como equilibradores de carga HTTP (nginx) y cachés (barniz).

Por otro lado, una interfaz uniforme:

  • degrada la eficiencia, porque la información se transfiere de forma estandarizada en lugar de una que sea específica para las necesidades de una aplicación.

Las compensaciones están "diseñadas para el caso común de la Web" y han permitido construir un gran ecosistema que proporciona soluciones a muchos de los problemas comunes en las arquitecturas web. Adherirse a una interfaz uniforme permitirá que su sistema se beneficie de este ecosistema, mientras que romperlo lo dificultará. Es posible que desee usar un equilibrador de carga como nginx, pero ahora solo puede usar un equilibrador de carga que comprenda DRAFT y CURATE. Es posible que desee usar una capa de caché HTTP como Varnish, pero ahora solo puede usar una capa de caché HTTP que entienda DRAFT y CURATE. Es posible que desee pedir ayuda a alguien para solucionar un error del servidor, pero nadie más conoce la semántica de una solicitud CURATE. Puede ser difícil cambiar sus bibliotecas preferidas de cliente o servidor para comprender e implementar correctamente los nuevos métodos. Y así.

La forma correcta * de representar esto es como una transformación de estado en el recurso (o recursos relacionados). No DRAFT una publicación, transforma su draftestado trueo crea un draftrecurso que contiene los cambios y enlaces a versiones preliminares anteriores. No CURATE una publicación, transforma su curatedestado trueo crea un curationrecurso que vincula la publicación con el usuario que la seleccionó.

* Correcto porque sigue más de cerca los principios arquitectónicos REST.

Rein Henrichs
fuente
Gracias por los comentarios sobre el equilibrio de carga, definitivamente lo veré. ¿Conoces un recurso que indique si los métodos personalizados son aceptables o no?
Juan Mendes
2
No veo ninguna ventaja en los métodos personalizados a menos que sean parte de una extensión ampliamente compatible como WEBDAV (e incluso entonces, no tanto), por lo que nunca lo he investigado. Solo recomendaría que trate estos cambios como transformaciones de estado. La web funciona bien con los métodos que ya tenemos. Realmente no hay una buena razón para agregar más a menos que tengan sentido como parte de la interfaz uniforme (como PATCH).
Rein Henrichs
55
Veo la ventaja de diseñar la forma en que desea que su servicio HTTP funcione para usted. sin embargo, "no se puede suponer que los métodos adicionales compartan la misma semántica". Bastante justo, PERO todavía es parte del alcance HTTP / 1.1, por lo tanto, los firewalls, los servidores proxy, los equilibradores de carga y similares deberían permitir esta posibilidad, si no lo hacen. ¿Entonces no están implementando HTTP / 1.1 correctamente?
Prof83
Probablemente tengas razón desde el punto de vista resty, sin embargo, no puedo ver por qué los verbos personalizados deberían ser un problema. Todas las herramientas deben tratarlas como POST, es decir, "el recurso probablemente cambia y eso es todo lo que sabemos".
maaartinus
7

Preferiría diseñar estos como subrecursos, en los que realiza una solicitud POST.

Considere que tiene un recurso en /instance/type/1, me gustaría que la representación de ese recurso transmita un par de enlaces a 'acciones' que se pueden realizar en el recurso, como /instance/type/1/drafty /instance/type/1/curate. En JSON, esto podría ser tan simple como:

{
    "some property":"the usual value",
    "state": "we can still inform the client about the current state",
    "draft": "http://server/instance/type/1/draft",
    "curate": "http://server/instance/type/1/curate"
}

Esto permite que el cliente sea muy explícito acerca de lo que debe suceder, durante la solicitud POST al enlace proporcionado por el curatemiembro. El recurso publicado allí podría incluir argumentos que detallen el evento que, tal vez, infligiría una transición de estado.

Seguir el enfoque 'ingenuo' de moverse entre los posibles estados en un recurso tiene la desventaja de no capturar qué eventos condujeron a estas transiciones.

Las transiciones de estado generalmente ocurren en respuesta a eventos específicos, y prefiero capturar esos eventos que dejar que el cliente decida que algo está ahora en un "estado" específico. También hace que la validación sea mucho más difícil. Además, no podrá capturar ningún 'argumento' a menos que también describa los del estado mismo. Y luego todo se vuelve desagradable cuando algunos códigos cambian aquellos sin transición de estado real, y se requiere la validación, y todo se convierte rápidamente en un desastre.

Dave Van den Eynde
fuente
Buena respuesta. Poder proporcionar argumentos para las transiciones de estado y hacer que el servidor encapsule y administre estos es, con mucho, el mejor enfoque.
Thomas W
La compañía en la que estoy actualmente (VMware) hace esto. Un GET en /vms/some-iddevuelve enlaces a acciones como POST /vms/some-id/restarty lo usamos para determinar si las acciones deben habilitarse o deshabilitarse. Tengo una relación de amor / odio con HATEOAS :)
Juan Mendes
Tendría mucho más sentido si la acción que se tomara fuera el verbo de la solicitud frente a algún parámetro de consulta aleatorio, segmento de ruta de recursos o propiedad del cuerpo.
Matthew Whited
No puedes vincular a un verbo.
Dave Van den Eynde
6

Creo que el método HTTP personalizado es la mejor manera de implementar acciones de entidad. Agregar la acción al cuerpo de la entidad (POST) no parece correcto, no es parte de su entidad (aunque el resultado podría guardarse en él). Además, el uso de proxies de métodos HTTP personalizados podría determinar sus acciones sin la necesidad de analizar el cuerpo de la entidad.

Es como CRUD, siempre querrás implementarlos, pero también tienes tu propio conjunto específico de acciones (por entidad). Realmente no veo cuál sería el problema para extenderlos.

También @Rein Henrichs "No DIBUJAS una publicación, transformas su estado borrador en verdadero o creas un recurso borrador" me parece falso. Se draftsusaría una propiedad para guardar el estado de forma persistente, no para realizar la transformación. Las acciones ni siquiera resultan necesariamente en un 'estado' o se guardan en una propiedad. Crear una entidad separada para cada estado / transformación parece aún más confuso ... intente mantener la misma referencia (URI) a la entidad.

Robert de W
fuente
1
Este es un punto justo, aunque está muy en desacuerdo, puedo ver el razonamiento detrás de esto y no estoy de acuerdo con el voto negativo (especialmente sin comentarios del votante). Tomemos como ejemplo el manejo de excepciones PHP, "Best Practice" parece inclinarse hacia el uso de tipos de excepción específicos para sugerir el tipo de excepción, incluso ignorando el mensaje real como RuntimeException vs BadMethodCallException. Entonces, ¿por qué se discute tanto el uso de DRAFT si el uso de métodos personalizados ya se considera parte del alcance HTTP / 1.1? Y los equilibradores de carga y los representantes también deberían aceptar esta posibilidad también
Prof83