HTTP POST con parámetros de consulta de URL: ¿buena idea o no? [cerrado]

451

Estoy diseñando una API para ir sobre HTTP y me pregunto si usar el comando HTTP POST, pero solo con parámetros de consulta de URL y sin cuerpo de solicitud, es una buena manera de hacerlo.

Consideraciones:

  • El "buen diseño web" requiere que se envíen acciones no idempotentes a través de POST. Esta es una acción no idempotente.
  • Es más fácil desarrollar y depurar esta aplicación cuando los parámetros de solicitud están presentes en la URL.
  • La API no está destinada a un uso generalizado.
  • Parece que realizar una solicitud POST sin cuerpo requerirá un poco más de trabajo, por ejemplo, se Content-Length: 0debe agregar explícitamente un encabezado.
  • También me parece que un POST sin cuerpo es un poco contrario a las expectativas de la mayoría de los desarrolladores y los marcos HTTP.

¿Hay más dificultades o ventajas para enviar parámetros en una solicitud POST a través de la consulta URL en lugar del cuerpo de la solicitud?

Editar: La razón por la que esto se está considerando es que las operaciones no son idempotentes y tienen otros efectos secundarios además de la recuperación. Ver la especificación HTTP :

En particular, la convención ha establecido que los métodos GET y HEAD NO DEBEN tener la importancia de tomar una acción que no sea la recuperación. Estos métodos deben considerarse "seguros". Esto permite a los agentes de usuario representar otros métodos, como POST, PUT y DELETE, de una manera especial, para que el usuario sea consciente del hecho de que se está solicitando una acción posiblemente insegura.

...

Los métodos también pueden tener la propiedad de "idempotencia" en que (aparte de los problemas de error o caducidad) los efectos secundarios de N> 0 solicitudes idénticas son las mismas que para una solicitud única. Los métodos GET, HEAD, PUT y DELETE comparten esta propiedad. Además, los métodos OPTIONS y TRACE NO DEBEN tener efectos secundarios, por lo que son inherentemente idempotentes.

Steven Huwig
fuente
11
¿Por qué usar POST en absoluto si no vas a proporcionar datos en el cuerpo?
Sunny Milenov
114
Porque la operación no es idempotente.
Steven Huwig
20
@Jared, tenga en cuenta que la palabra "REST" no aparece en esta pregunta de hace 2,5 años. :) La especificación HTTP sobre idempotencia se aplica independientemente de cuál sea la arquitectura de sabor del mes para los servicios web. Afortunadamente, el sistema para el que se diseñó esta API para proxy se ha vuelto obsoleto de todos modos.
Steven Huwig
55
Debido a que los registros del servidor no registran los parámetros POST, sino que registran las cadenas de consulta. Es mucho más fácil ejecutar la serie de solicitudes sin instrumentarla en el navegador, y luego mirar el rastreo, que hacer clic en ellas. Además, la API no era de navegador a servidor, sino de servidor a servidor. Lo más importante, todo el asunto fue conservado de todos modos. :)
Steven Huwig
13
Para cualquiera que no sepa lo que significa idempotente: | restapitutorial.com/lessons/idempotency.html
Christopher Grigg

Respuestas:

259

Si su acción no es idempotente, DEBE usarla POST. Si no lo hace, solo está pidiendo problemas en el futuro. GET, PUTy DELETEse requieren métodos para ser idempotentes. Imagine lo que sucedería en su aplicación si el cliente buscara previamente todas las GETsolicitudes posibles de su servicio; si esto causara efectos secundarios visibles para el cliente, entonces algo está mal.

Estoy de acuerdo en que enviar un POSTcon una cadena de consulta pero sin un cuerpo parece extraño, pero creo que puede ser apropiado en algunas situaciones.

Piense en la parte de consulta de una URL como un comando del recurso para limitar el alcance de la solicitud actual. Por lo general, las cadenas de consulta se usan para ordenar o filtrar una GETsolicitud (me gusta ?page=1&sort=title), pero supongo que tiene sentido POSTlimitar también el alcance (tal vez me gusta ?action=delete&id=5).

Don McCaughey
fuente
44
Seleccioné esta respuesta para este caso particular, pero creo que el argumento de R. Bemrose es convincente para las API públicas.
Steven Huwig
44
No creo que su respuesta sea estrictamente correcta. Si conoce los parámetros de URL para la publicación de su formulario cuando se envía la página HTML al cliente, puede agregar esos parámetros de URL al atributo de acción del formulario; de lo contrario, JavaScript puede establecer los parámetros de URL cuando se envíe el formulario.
Don McCaughey
3
¿qué tal una publicación de un archivo xml en una url con parámetros de consulta? ¿es eso posible?
OpenCoderX
3
Otro ejemplo: los datos de la solicitud podrían estar en la entidad http, mientras que el formato solicitado de la respuesta se pasa en el parámetro de consulta ( /action?response_format=json)
rds
44
+1 aprendí algo hoy. Eliminar tiene un tecnicismo para ser idempotente. Si el objeto se elimina realmente, obtendrá un 404 no encontrado, por lo que el servidor tendrá el mismo estado pero la respuesta será diferente. Vea la imagen de la vaca: restapitutorial.com/lessons/idempotency.html
JPK
131

Todos tienen razón: quédese con POST para solicitudes no idempotentes.

¿Qué pasa con el uso de una cadena de consulta URI y contenido de solicitud? Bueno, es HTTP válido (ver nota 1), ¿por qué no?

También es perfectamente lógico: las URL, incluida su parte de cadena de consulta, son para localizar recursos. Mientras que los verbos del método HTTP (POST - y su contenido de solicitud opcional) son para especificar acciones o qué hacer con los recursos. Esas deberían ser preocupaciones ortogonales. (Pero, no son preocupaciones maravillosamente ortogonales para el caso especial de ContentType = application / x-www-form-urlencoded, vea la nota 2 a continuación).

Nota 1: la especificación HTTP (1.1) no establece que los parámetros de consulta y el contenido sean mutuamente exclusivos para un servidor HTTP que acepte solicitudes POST o PUT. Por lo tanto, cualquier servidor es libre de aceptar ambos. Es decir, si escribe el servidor, no hay nada que le impida elegir aceptar ambos (excepto tal vez un marco inflexible). Generalmente, el servidor puede interpretar cadenas de consulta de acuerdo con las reglas que desee. Incluso puede interpretarlos con lógica condicional que se refiere a otros encabezados como Content-Type también, lo que lleva a la Nota 2:

Nota 2: si un navegador web es la forma principal en que las personas acceden a su aplicación web, y application / x-www-form-urlencoded es el Tipo de contenido que publican, entonces debe seguir las reglas para ese Tipo de contenido. Y las reglas para application / x-www-form-urlencoded son mucho más específicas (y, francamente, inusuales): en este caso, debe interpretar el URI como un conjunto de parámetros, y no como una ubicación de recursos. [Este es el mismo punto de utilidad que Powerlord planteó; que puede ser difícil usar formularios web para PUBLICAR contenido en su servidor. Acabo de explicar un poco diferente.]

Nota 3: ¿para qué sirven originalmente las cadenas de consulta? RFC 3986 define cadenas de consulta HTTP como una parte de URI que funciona como una forma no jerárquica de localizar un recurso.

En caso de que los lectores que hagan esta pregunta deseen preguntar qué es una buena arquitectura RESTful: el patrón de arquitectura RESTful no requiere esquemas URI para funcionar de manera específica. La arquitectura RESTful se ocupa de otras propiedades del sistema, como la capacidad de almacenamiento en caché de los recursos, el diseño de los propios recursos (su comportamiento, capacidades y representaciones) y si se satisface la idempotencia. O, en otras palabras, lograr un diseño que sea altamente compatible con el protocolo HTTP y su conjunto de verbos de métodos HTTP. :-) (En otras palabras, la arquitectura RESTful no es muy preceptiva con la forma en que se encuentran los recursos) .

Nota final: a veces los parámetros de consulta se utilizan para otras cosas, que no son ni localizar recursos ni codificar contenido. ¿Alguna vez has visto un parámetro de consulta como 'PUT = true' o 'POST = true'? Estas son soluciones para navegadores que no le permiten usar los métodos PUT y POST. Si bien dichos parámetros se ven como parte de la cadena de consulta de URL (en el cable), sostengo que no son parte de la consulta de la URL en espíritu .

Tim Lovell-Smith
fuente
66

Quieres razones? Aquí hay uno:

No se puede usar un formulario web para enviar una solicitud a una página que usa una combinación de GET y POST. Si establece el método del formulario en GET, todos los parámetros están en la cadena de consulta. Si establece el método del formulario en POST, todos los parámetros están en el cuerpo de la solicitud.

Fuente: estándar HTML 4.01, sección 17.13 Envío de formularios

Powerlord
fuente
10
Es un argumento decente, pero creo que las implementaciones de JavaScript del navegador moderno hacen que sea un punto discutible. Sin embargo, lo pensaré: es convincente de una manera a prueba de futuro. El hecho de que no esté usando un formulario ahora no significa que no quiera hacerlo más tarde.
Steven Huwig
99
Mezclar GET con POST es solo una muy mala idea: romper terriblemente HTTP y sin ninguna buena razón.
aehlke 03 de
66
Ese fragmento no aparece en la página a la que se vinculó
Gareth,
40
Correcto, pero el atributo del método solo define cómo se incluye el "conjunto de datos de formulario" en la solicitud. Cuando methodes POST, no se menciona el cambio del URI en el formulario action. Y, por supuesto, cualquier URI ya puede contener una parte de cadena de consulta.
Gareth
16
@Powerlord Esto está mal. Intente configurar un formulario para PUBLICAR con una acción de, por ejemplo. /Books?bookCode=1234. El servidor web obtendrá vars de formulario POST y una cadena de consulta.
Jez
9

Desde un punto de vista programático, para el cliente está empacando parámetros y agregándolos a la url y realizando un POST frente a un GET. En el lado del servidor, está evaluando los parámetros entrantes de la cadena de consulta en lugar de los bytes publicados. Básicamente, es un lavado.

Donde podría haber ventajas / desventajas podría estar en cómo funcionan las plataformas de cliente específicas con las rutinas POST y GET en su pila de redes, así como también cómo el servidor web maneja esas solicitudes. Dependiendo de su implementación, un enfoque puede ser más eficiente que el otro. Saber eso guiaría su decisión aquí.

Sin embargo, desde la perspectiva de un programador, prefiero permitir una POST con todos los parámetros en el cuerpo o una GET con todos los parámetros en la url, e ignorar explícitamente los parámetros de la url con cualquier solicitud POST. Evita la confusión.

jro
fuente
8

Creo que aún podría ser bastante RESTful tener argumentos de consulta que identifiquen el recurso en la URL mientras se mantiene la carga útil de contenido confinada al cuerpo POST. Esto parecería separar las consideraciones de "¿Qué estoy enviando?" versus "¿A quién se lo envío?".

swizzcheez
fuente
55
La pregunta no era sobre REST.
Steven Huwig
3
@ user359996 No todas las API HTTP son RESTful. De hecho, la mayoría de las API que afirman que en realidad no lo son. Además, lo curioso es que REST tampoco es solo HTTP.
Alec Mev
4

El campamento REST tiene algunos principios rectores que podemos usar para estandarizar la forma en que usamos los verbos HTTP. Esto es útil al construir API RESTful como lo está haciendo.

En pocas palabras: GET debe ser de solo lectura, es decir, no tener ningún efecto en el estado del servidor. POST se utiliza para crear un recurso en el servidor. PUT se usa para actualizar o crear un recurso. DELETE se usa para eliminar un recurso.

En otras palabras, si su acción de API cambia el estado del servidor, REST nos aconseja usar POST / PUT / DELETE, pero no GET.

Los agentes de usuario generalmente entienden que hacer múltiples POST es malo y lo advertirán, porque la intención de POST es alterar el estado del servidor (por ejemplo, pagar los productos al momento del pago), ¡y probablemente no quieras hacerlo dos veces!

Compare con un GET que puede hacer a menudo como quiera (idempotente).

saille
fuente
13
El campo REST dice que debe usar HTTP como se define en la especificación HTTP. es decir, RFC2616 Nada más, nada menos.
Darrel Miller el
1
@Darrel Refering ibm.com/developerworks/webservices/library/ws-restful : REST pide a los desarrolladores que usen métodos HTTP explícitamente y de una manera que sea consistente con la definición del protocolo. Este principio de diseño REST básico establece un mapeo uno a uno entre las operaciones de creación, lectura, actualización y eliminación (CRUD) y los métodos HTTP. De acuerdo con esta asignación: para crear un recurso en el servidor, use POST. Para recuperar un recurso, use GET. Para cambiar el estado de un recurso o actualizarlo, use PUT. Para eliminar o eliminar un recurso, use DELETE.
saille
55
Lo siento, pero eso es simplemente incorrecto. REST requiere el cumplimiento de una interfaz uniforme. Si está utilizando HTTP, entonces esa interfaz uniforme se define en parte por RFC 2616. En esa especificación, no existe una asignación uno a uno entre crear, leer, actualizar y eliminar y los métodos HTTP.
Darrel Miller el
3
OBTENER y BORRAR el mapa bastante bien para Leer y Eliminar en CRUD, pero usar PUT / POST para Actualizar y Crear no es tan sencillo. Ver stackoverflow.com/questions/630453/put-vs-post-in-rest
dcstraw
55
Mirando hacia atrás en esto 6 años después, y dado que la pregunta se ha visto ~ 100k veces, siento que vale la pena poner una pequeña actualización. Darrel es correcto según la definición de REST de Fielding ( ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm ): no se menciona la asignación de verbos HTTP a CRUD. El consejo del desarrollador de IBM (enlace en el comentario anterior) refleja la práctica común en la implementación de API RESTful, no la definición de REST de Fielding.
saille
-13

Estoy de acuerdo: probablemente sea más seguro usar una solicitud GET si solo está pasando datos en la URL y no en el cuerpo. Vea esta pregunta similar para obtener algunas vistas adicionales sobre todo el concepto POST + GET.

Marc Novakowski
fuente
17
Si la operación tiene efectos secundarios, ciertamente no es "más seguro" usar el método GET porque el navegador asume que todos los GET son idempotentes.
dcstraw
Los motores de búsqueda también lo convierten en una pesadilla, ya que Google "hará clic" de forma segura en todos los enlaces con la solicitud GET, pero omitirá todo lo demás. No es tan seguro abandonar un servicio que un rastreador inocente puede borrar accidentalmente la base de datos.
Alejandro