HTTP GET con cuerpo de solicitud

Estoy desarrollando un nuevo servicio web RESTful para nuestra aplicación.

Al hacer un GET en ciertas entidades, los clientes pueden solicitar los contenidos de la entidad. Si desean agregar algunos parámetros (por ejemplo, ordenar una lista), pueden agregar estos parámetros en la cadena de consulta.

Alternativamente, quiero que las personas puedan especificar estos parámetros en el cuerpo de la solicitud. HTTP / 1.1 no parece prohibir explícitamente esto. Esto les permitirá especificar más información, podría facilitar la especificación de solicitudes XML complejas.

Mis preguntas:

  • ¿Es esta una buena idea?
  • ¿Tendrán problemas los clientes HTTP con el uso de cuerpos de solicitud dentro de una solicitud GET?

http://tools.ietf.org/html/rfc2616

Comentario de Roy Fielding sobre incluir un cuerpo con una solicitud GET .

Sí. En otras palabras, cualquier mensaje de solicitud HTTP puede contener un cuerpo de mensaje y, por lo tanto, debe analizar los mensajes teniendo esto en cuenta. Sin embargo, la semántica del servidor para GET está restringida, de modo que un cuerpo, si lo hay, no tiene ningún significado semántico para la solicitud. Los requisitos para el análisis son independientes de los requisitos de la semántica de los métodos.

Entonces, sí, puedes enviar un cuerpo con GET, y no, nunca es útil hacerlo.

Esto es parte del diseño estratificado de HTTP / 1.1 que se volverá a aclarar una vez que la especificación esté particionada (trabajo en progreso).

…. Roy

Sí, puedes enviar un cuerpo de solicitud con GET pero no debería tener ningún significado. Si le da significado analizándolo en el servidor y cambiando su respuesta en función de su contenido , entonces está ignorando esta recomendación en la especificación HTTP / 1.1, sección 4.3 :

[…] si el método de solicitud no incluye una semántica definida para un cuerpo-entidad, entonces el cuerpo del mensaje DEBERÍA ser ignorado cuando se maneje la solicitud.

Y la descripción del método GET en la especificación HTTP / 1.1, sección 9.3 :

El método GET significa recuperar cualquier información ([…]) identificada por Request-URI.

que indica que el cuerpo de solicitud no es parte de la identificación del recurso en una solicitud GET, solo el URI de solicitud.

Si bien puede hacerlo, en la medida en que no esté explícitamente excluido por la especificación HTTP, sugeriría evitarlo simplemente porque las personas no esperan que las cosas funcionen de esa manera. Hay muchas fases en una cadena de solicitud de HTTP y, aunque “en su mayoría” se ajustan a las especificaciones de HTTP, lo único que se le garantiza es que se comportarán como lo usan tradicionalmente los navegadores web. (Estoy pensando en cosas como proxies transparentes, aceleradores, kits de herramientas A / V, etc.)

Este es el espíritu detrás del Principio de Robustez: ” Sé liberal en lo que aceptas y conservador en lo que envías”, no querrás superar los límites de una especificación sin una buena razón.

Sin embargo, si tienes una buena razón, ve por ello.

Es probable que encuentre problemas si alguna vez intenta aprovechar el almacenamiento en caché. Los proxies no buscarán en el cuerpo GET para ver si los parámetros tienen un impacto en la respuesta.

Ni el cliente de reposo ni la consola REST admiten esto, pero Curl sí lo hace.

La especificación HTTP dice en la sección 4.3

Un cuerpo de mensaje NO DEBE incluirse en una solicitud si la especificación del método de solicitud (sección 5.1.1) no permite el envío de un cuerpo de entidad en las solicitudes.

La Sección 5.1.1 nos redirige a la sección 9.x para los diversos métodos. Ninguno de ellos prohíbe explícitamente la inclusión de un cuerpo de mensaje. Sin embargo…

La sección 5.2 dice

El recurso exacto identificado por una solicitud de Internet se determina examinando tanto el URI de solicitud como el campo del encabezado del host.

y la Sección 9.3 dice

El método GET significa recuperar cualquier información (en forma de una entidad) identificada por Request-URI.

Que en conjunto sugieren que al procesar una solicitud GET, un servidor no está obligado a examinar cualquier otra cosa que el campo URI de solicitud y encabezado de host.

En resumen, la especificación HTTP no evita que envíe un cuerpo de mensaje con GET, pero hay suficiente ambigüedad como para no sorprenderme si no fuera compatible con todos los servidores.

Elasticsearch acepta solicitudes GET con un cuerpo. Incluso parece que esta es la forma preferida: http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/common-options.html#_request_body_in_query_string

Algunas bibliotecas de cliente (como el controlador Ruby) pueden registrar el comando cry en stdout en modo de desarrollo y está usando esta syntax de manera extensiva.

¿Qué servidor lo ignorará? – Fijiaaron 30 de agosto de 12 a 21:27

Google, por ejemplo, lo está haciendo peor que ignorarlo, ¡lo considerará un error !

Pruébelo usted mismo con un netcat simple:

 $ netcat www.google.com 80 GET / HTTP/1.1 Host: www.google.com Content-length: 6 1234 

(El contenido 1234 es seguido por CR-LF, por lo que es un total de 6 bytes)

y obtendrás:

 HTTP/1.1 400 Bad Request Server: GFE/2.0 (....) Error 400 (Bad Request) 400. That's an error. Your client has issued a malformed or illegal request. That's all we know. 

También recibes 400 Bad Request de Bing, Apple, etc. que son atendidos por AkamaiGhost.

Así que no aconsejaría usar solicitudes GET con una entidad física.

Lo que está tratando de lograr se ha hecho durante mucho tiempo con un método mucho más común, y uno que no se basa en el uso de una carga útil con GET.

Simplemente puede comstackr su tipo de medio de búsqueda específico, o si quiere ser más RESTful, use algo como OpenSearch y envíe la solicitud al URI que el servidor le indicó, por ejemplo, / search. El servidor puede generar el resultado de búsqueda o construir el URI final y redirigir utilizando un 303.

Esto tiene la ventaja de seguir el método PRG tradicional, ayuda a los intermediarios de caché a almacenar en caché los resultados, etc.

Dicho esto, los URI están codificados de todos modos para cualquier cosa que no sea ASCII, y también lo son application / x-www-form-urlencoded y multipart / form-data. Recomiendo usar esto en lugar de crear otro formato json personalizado si tu intención es admitir escenarios ReSTful.

Puedes enviar un GET con un cuerpo o enviar un mensaje POST y renunciar a la religiosidad REST (no es tan malo, hace 5 años solo había un miembro de esa fe, sus comentarios se vincularon anteriormente).

Tampoco son buenas decisiones, pero enviar un cuerpo GET puede evitar problemas para algunos clientes, y algunos servidores.

Hacer un POST puede tener obstáculos con algunos frameworks RESTish.

Julian Reschke sugirió anteriormente el uso de un encabezado HTTP no estándar como “SEARCH”, que podría ser una solución elegante, excepto que es aún menos probable que sea compatible.

Puede ser más productivo listar clientes que pueden y no pueden hacer cada uno de los anteriores.

Clientes que no pueden enviar un GET con cuerpo (que yo sepa):

  • XmlHTTPRequest Fiddler

Clientes que pueden enviar un GET con cuerpo:

  • la mayoría de los navegadores

Servidores y bibliotecas que pueden recuperar un cuerpo de GET:

  • apache
  • PHP

Servidores (y proxies) que despojan a un cuerpo de GET:

  • ?

De RFC 2616, sección 4.3 , “Cuerpo del mensaje”:

Un servidor DEBERÍA leer y reenviar un cuerpo de mensaje en cualquier solicitud; si el método de solicitud no incluye una semántica definida para un cuerpo de entidad, entonces el cuerpo del mensaje DEBERÍA ser ignorado al manejar la solicitud.

Es decir, los servidores siempre deben leer cualquier cuerpo de solicitud proporcionado de la red (verificar Content-Length o leer un cuerpo fragmentado, etc.). Además, los apoderados deben enviar cualquier cuerpo de solicitud que reciban. Entonces, si el RFC define la semántica del cuerpo para el método dado, el servidor puede usar el cuerpo de la solicitud para generar una respuesta. Sin embargo, si el RFC no define semántica para el cuerpo, entonces el servidor debería ignorarlo.

Esto está en línea con la cita de Fielding arriba.

La Sección 9.3 , “GET”, describe la semántica del método GET y no menciona los cuerpos de solicitud. Por lo tanto, un servidor debe ignorar cualquier cuerpo de solicitud que recibe en una solicitud GET.

Puse esta pregunta al IETF HTTP WG. El comentario de Roy Fielding (autor del documento http / 1.1 en 1998) fue que

“… una implementación se rompería para hacer algo más que analizar y descartar ese cuerpo si se recibe”

RFC 7213 (HTTPbis) declara:

“Una carga útil dentro de un mensaje de solicitud GET no tiene semántica definida;”

Parece claro ahora que la intención era que el significado semántico en los cuerpos de solicitud GET está prohibido, lo que significa que el cuerpo de la solicitud no puede utilizarse para afectar el resultado.

Hay proxies que definitivamente romperán su solicitud de varias maneras si incluye un cuerpo en GET.

Entonces, en resumen, no lo hagas.

Si realmente desea enviar un cuerpo JSON / XML en caché a la aplicación web, el único lugar razonable para colocar sus datos es la cadena de consulta codificada con RFC4648: Codificación Base 64 con URL y nombre de archivo Alfabeto seguro . Por supuesto, podría simplemente urlencode JSON y poner está en el valor de URL param, pero Base64 da un resultado más pequeño. Tenga en cuenta que existen restricciones de tamaño de URL, consulte ¿Cuál es la longitud máxima de una URL en diferentes navegadores? .

Puede pensar que el carácter relleno de Base64 = puede ser malo para el valor de param de la URL, sin embargo, parece que no – vea esta discusión: http://mail.python.org/pipermail/python-bugs-list/2007-February/037195.html . Sin embargo, no debe poner datos codificados sin nombre de parámetro porque la cadena codificada con relleno se interpretará como clave param con valor vacío. Yo usaría algo como ?_b64= .

Me molesta que REST ya que el protocolo no es compatible con OOP y el método Get es una prueba. Como solución, puede serializar su DTO a JSON y luego crear una cadena de consulta. Del lado del servidor, podrás deserializar la cadena de consulta al DTO.

Echa un vistazo a:

  • Diseño basado en mensajes en ServiceStack
  • Creación de servicios web basados ​​en mensajes RESTful con WCF

El enfoque basado en mensajes puede ayudarlo a resolver la restricción de obtener el método. Podrá enviar cualquier OTD como con el cuerpo de la solicitud

El marco de servicios web de Nelibur proporciona una funcionalidad que puede usar

 var client = new JsonServiceClient(Settings.Default.ServiceAddress); var request = new GetClientRequest { Id = new Guid("2217239b0e-b35b-4d32-95c7-5db43e2bd573") }; var response = client.Get(request); as you can see, the GetClientRequest was encoded to the following query string http://localhost/clients/GetWithResponse?type=GetClientRequest&data=%7B%22Id%22:%2217239b0e-b35b-4d32-95c7-5db43e2bd573%22%7D 

¿Qué pasa con los encabezados codificados en base64 no conformes? “SOMETHINGAPP-PARAMS: sdfSD45fdg45 / aS”

Restricciones de longitud hm. ¿No puedes hacer que tu manejo POST distinga entre los significados? Si desea parámetros simples como ordenar, no veo por qué esto sería un problema. Supongo que es seguro que estás preocupado.

En mi humilde opinión, podría simplemente enviar el JSON codificado (es decir, encodeURIComponent ) en la URL , de esta manera no viola las especificaciones HTTP y obtener su JSON en el servidor.

No aconsejaría esto, va en contra de las prácticas estándar, y no ofrece mucho a cambio. Desea mantener el cuerpo para el contenido, no para las opciones.

Por ejemplo, funciona con Curl, Apache y PHP.

Archivo PHP:

  

Comando de la consola:

 $ curl -X GET -H "Content-Type: application/json" -d '{"the": "body"}' 'http://localhost/test/get.php' 

Salida:

 GET {"the": "body"} 

De acuerdo con XMLHttpRequest, no es válido. Del estándar :

4.5.6 El método send()

 client . send([ body = null]) 

Inicia la solicitud El argumento opcional proporciona el cuerpo de la solicitud. El argumento se ignora si el método de solicitud es GET o HEAD .

Lanza una excepción InvalidStateError si no se abre ningún estado o si se establece el indicador send() .

El método de send( body ) debe ejecutar estos pasos:

  1. Si el estado no está abierto , InvalidStateError una excepción InvalidStateError .
  2. Si se establece el indicador send() , InvalidStateError una excepción InvalidStateError .
  3. Si el método de solicitud es GET o HEAD , establezca body en null.
  4. Si el cuerpo es nulo, vaya al siguiente paso.

Aunque, no creo que deba hacerlo porque la solicitud GET podría necesitar un gran contenido corporal.

Por lo tanto, si confía en XMLHttpRequest de un navegador, es probable que no funcione.

Google, IBM, Microsoft y apigee parecen usar encabezado para designar el método realmente esperado, algo así como X-HTTP-Method-Override: GET.
De acuerdo con el blog anterior, originalmente, esto se introduce para POST como PUT en forma.
Creo que esta solución se puede usar en este problema.

En otras palabras, creo que POST con encabezado X-HTTP-Method-Override: GET es la mejor solución.