HTTP GET con cuerpo de solicitud

2112

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

Al hacer un GET en ciertas entidades, los clientes pueden solicitar el contenido 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 esto explícitamente. 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 por completo?
  • ¿Tendrán problemas los clientes HTTP con el uso de cuerpos de solicitud dentro de una solicitud GET?

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

Evert
fuente
553
La ventaja es que permite enviar fácilmente cuerpos de solicitud XML o JSON, no tiene una restricción de longitud y es más fácil de codificar (UTF-8).
Evert
29
Si lo que busca es un método seguro e idempotente que permita los cuerpos de solicitud, es posible que desee buscar en BÚSQUEDA, PROPONER e INFORMAR. Por supuesto, no usar GET y tener un cuerpo de solicitud derrota el almacenamiento en caché más o menos.
Julian Reschke
226
@fijiaaron: Han pasado 3 años, y desde entonces tengo una amplia experiencia en la redacción de servicios web. Es básicamente todo lo que he estado haciendo durante los últimos años. Puedo decir con seguridad que, de hecho, es una muy mala idea agregar un cuerpo a una solicitud GET. Las dos respuestas principales se destacan como una roca.
Evert
26
@Ellesedil: En pocas palabras: cualesquiera que sean las ventajas de usar GET sobre POST, existen debido a cómo está diseñado HTTP. Esas ventajas ya no existen cuando infringe el estándar de esta manera. Por lo tanto, solo queda una razón para usar GET + un cuerpo de solicitud en lugar de POST: Estética. No sacrifique el diseño robusto por la estética.
Evert
77
Para subrayar lo que dijo Evert: "no tiene una restricción de longitud". Si su GET con parámetros de consulta está rompiendo la restricción de longitud (de 2048), entonces, ¿qué otra opción hay además de poner la información de la cadena de consulta en un objeto json, por ejemplo, en el cuerpo de la solicitud.
Kieran Ryan

Respuestas:

1726

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

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

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

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

.... Roy

Sí, puede enviar un cuerpo de solicitud con GET pero no debería tener ningún significado. Si le da sentido al analizarlo en el servidor y cambiar 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 de entidad, entonces el cuerpo del mensaje DEBE ignorarse al manejar 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 el URI de solicitud.

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

Actualización El RFC2616 al que se hace referencia como "especificación HTTP / 1.1" ahora está obsoleto. En 2014 fue reemplazado por RFC 7230-7237. Se ha eliminado la cita "el cuerpo del mensaje DEBE ignorarse al manejar la solicitud". Ahora es solo "La estructura de mensajes de solicitud es independiente de la semántica del método, incluso si el método no define ningún uso para un cuerpo de mensaje" La segunda cita "El método GET significa recuperar cualquier información ... identificada por el URI de solicitud" fué borrado. - De un comentario

Paul Morgan
fuente
72
El almacenamiento en caché / proxy son las dos cosas que es más probable que rompa, sí. La "semántica" es solo otra forma de decir "la forma en que las personas que fabrican otros componentes esperan que funcionen otros componentes". Si viola la semántica, es más probable que vea que las cosas se rompen en lugares donde la gente escribió cosas que esperaban que honrara esa semántica.
Stuart P. Bentley
109
Elasticsearch es un producto bastante importante que utiliza cuerpos de solicitud HTTP en GET. Según su manual, si una solicitud HTTP debe admitir tener un cuerpo o no no está definida. Personalmente, no me siento cómodo rellenando un cuerpo de solicitud GET, pero parecen tener una opinión diferente y deben saber lo que están haciendo. elastic.co/guide/en/elasticsearch/guide/current/...
GordonM
26
@iwein dando sentido a los cuerpos de solicitud GET no es, de hecho, una violación de la especificación. HTTP / 1.1 especifica que los servidores DEBEN ignorar el cuerpo, pero RFC 2119 especifica que los implementadores pueden ignorar las cláusulas de "DEBE" si tienen una buena razón para hacerlo. Por el contrario, un cliente hace violar la especificación de si se asume que el cambio del cuerpo GET no cambiar la respuesta.
Emil Lundberg
108
El RFC2616 al que se hace referencia como "especificación HTTP / 1.1" ahora está obsoleto. En 2014 fue reemplazado por RFC 7230-7237. Se ha eliminado la cita " el cuerpo del mensaje DEBE ignorarse al manejar la solicitud " . Ahora es solo "La estructura de mensajes de solicitud es independiente de la semántica del método, incluso si el método no define ningún uso para un cuerpo de mensaje " La segunda cita " El método GET significa recuperar cualquier información ... identificada por el URI de solicitud " fue eliminada . Entonces, sugiero editar la respuesta @Jarl
Artem Nakonechny el
29
Sé que es un hilo viejo, me topé con él. @Artem Nakonechny tiene razón técnica, pero la nueva especificación dice "Una carga útil dentro de un mensaje de solicitud GET no tiene una semántica definida; enviar un cuerpo de carga útil en una solicitud GET podría causar que algunas implementaciones existentes rechacen la solicitud". Por lo tanto, aún no es una buena idea si se puede evitar.
fastcatch
290

Si bien puede hacerlo, en la medida en que no esté explícitamente excluido por la especificación HTTP, sugeriría evitarlo simplemente porque la gente no espera que las cosas funcionen de esa manera. Hay muchas fases en una cadena de solicitud HTTP y, si bien "en su mayoría" se ajustan a la especificación HTTP, lo único seguro es que se comportarán como los navegadores web utilizan tradicionalmente. (Estoy pensando en cosas como proxies transparentes, aceleradores, kits de herramientas de 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 quieres empujar los límites de una especificación sin una buena razón.

Sin embargo, si tiene una buena razón, hágalo.

caskey
fuente
229
El principio de robustez es defectuoso. Si eres liberal en lo que aceptas, obtendrás basura, si tienes algún éxito en términos de adopción, solo porque aceptas basura. Eso hará que le resulte más difícil evolucionar su interfaz. Solo mira HTML. Ese es el principio de reaparición en acción.
Eugene Beresovsky
28
Creo que el éxito y la amplitud de la adopción (y el abuso) de los protocolos hablan del valor del principio de robustez.
caskey
39
¿Alguna vez has intentado analizar HTML real? No es factible implementarlo usted mismo, es por eso que casi todos, incluidos los jugadores realmente grandes como Google (Chrome) y Apple (Safari), no lo hicieron, sino que confiaron en las implementaciones existentes (al final, todos confiaron en KHTML de KDE). Esa reutilización es, por supuesto, agradable, pero ¿ha intentado mostrar html en una aplicación .net? Es una pesadilla, ya que tiene que incrustar un componente IE (o similar) no administrado, con sus problemas y bloqueos, o usar el componente administrado disponible (en codeplex) que ni siquiera le permite seleccionar texto.
Eugene Beresovsky
66
La especificación HTTP no solo permite datos corporales con solicitud GET, sino que también es una práctica común: la popular API _search del motor ElasticSearch recomienda solicitudes GET con la consulta adjunta en un cuerpo JSON. Como concesión a implementaciones incompletas de clientes HTTP, también permite solicitudes POST aquí.
Christian Pietsch
44
@ChristianPietsch, es una práctica común hoy en día. Hace cuatro años no fue así. Si bien la especificación permite explícitamente que un cliente incluya opcionalmente (MAYO) una entidad en una solicitud (sección 7), el significado de MAYO se define en RFC2119 y un servidor proxy (malo) podría cumplir con las especificaciones al eliminar entidades en solicitudes GET, específicamente, siempre que no se bloquee, puede proporcionar 'funcionalidad reducida' al reenviar los encabezados de solicitud y no la entidad incluida. Del mismo modo, hay una gran cantidad de reglas sobre qué cambios de versión DEBEN / PUEDEN / DEBE realizarse al proxy entre diferentes niveles de protocolo.
caskey
151

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

Darrel Miller
fuente
10
El uso de los campos de encabezado ETag / Last-Modified ayuda de esta manera: cuando se usa un "GET condicional", los proxies / cachés pueden actuar sobre esta información.
jldupont
2
@jldupont Las memorias caché utilizan la presencia de validadores para saber si una respuesta obsoleta se puede volver a validar, sin embargo, no se usan como parte de la clave de memoria caché primaria o secundaria.
Darrel Miller
Puede solucionarlo con una suma de comprobación del cuerpo en un parámetro de consulta
Adrian puede el
73

Ni restclient ni la consola REST lo admiten, pero curl sí.

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 enviar 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 campo URI de solicitud como el campo de encabezado de host.

y la Sección 9.3 dice

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

Lo que en conjunto sugiere que cuando se procesa una solicitud GET, no se requiere que un servidor examine nada más que el campo URI de solicitud y encabezado de host.

En resumen, la especificación HTTP no le impide enviar un cuerpo de mensaje con GET, pero hay suficiente ambigüedad que no me sorprendería si no fuera compatible con todos los servidores.

Dave Durbin
fuente
2
Paw también tiene la opción de admitir solicitudes GET con cuerpos, pero debe habilitarse en la configuración.
s.Daniel
"El método GET significa recuperar cualquier información (en forma de entidad) identificada por el URI de solicitud". Entonces, ¿es técnicamente ilegal / incorrecto tener un punto final GET que obtenga todas las entidades? Por ejemplo, GET /contacts/100/addressesdevuelve una colección de direcciones para la persona con id=100.
Josh M.
La biblioteca Java tranquila para probar las API REST no admite la solicitud GET con un cuerpo. Apache HttpClient tampoco lo admite.
Paulo Merson
Django también admite el análisis de un cuerpo GET
Bruno Finger
60

Elasticsearch acepta solicitudes GET con un cuerpo. Incluso parece que esta es la forma preferida: guía Elasticsearch

Algunas bibliotecas de clientes (como el controlador Ruby) pueden registrar el comando cry en stdout en modo de desarrollo y está utilizando esta sintaxis ampliamente.

jlecour
fuente
55
Me preguntaba por qué Elasticsearch lo permite. Eso significa que esta consulta para contar todos los documentos con carga útil en una solicitud GET curl -XGET 'http://localhost:9200/_count?pretty' -d ' { "query": { "match_all": {} } }' es equivalente a incluir la carga útil como sourceparámetro: curl -XGET 'http://localhost:9200/_count?pretty&source=%7B%22query%22%3A%7B%22match_all%22%3A%7B%7D%7D%7D'
Arun
40
Las consultas complejas pueden alcanzar la longitud máxima del encabezado http.
s.Daniel
11
Fue leer la documentación de Elasticsearch lo que me llevó a esta pregunta, ya que pensé que se consideraba una mala práctica incluir un cuerpo
PatrickWalker
1
Ni siquiera necesita ser una consulta compleja. Incluso un simple desplazamiento puede devolver un scroll_id muy largo (en un clúster con muchos fragmentos), lo que conducirá a sobrepasar la longitud máxima de la url si se agrega allí.
Brent Hronik
11
Elasticsearch admite la misma solicitud mediante POST. Solo eligieron permitir un cuerpo en un GET porque sentían que un GET es más semánticamente correcto que un POST cuando se trata de consultar datos. Es curioso que Elasticsearch se mencione tanto en este hilo. No usaría un ejemplo (aunque sea de un producto popular) como una razón para seguir la práctica.
DSO
33

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 depende del uso de una carga útil con GET.

Simplemente puede crear su tipo de medio de búsqueda específico, o si desea ser más RESTful, use algo como OpenSearch y PUBLIQUE la solicitud al URI que el servidor le indicó, digamos / search. El servidor puede generar el resultado de búsqueda o construir el URI final y redirigir usando un 303.

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

Dicho esto, los URI se codifican 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 su intención es admitir escenarios ReSTful.

SerialSeb
fuente
44
Simplemente puede crear su tipo de medio de búsqueda específico ¿ Podría elaborarlo?
Piotr Dobrogost
2
Con eso estaba diciendo que podría crear un tipo de medio llamado application / vnd.myCompany.search + json que contendría el tipo de plantilla de búsqueda que desea que emita un cliente, y el cliente podría enviarlo como POST. Como he destacado, ya hay un tipo de medio para eso y se llama OpenSearch, la reutilización de un tipo de medio existente debe elegirse en la ruta personalizada cuando puede implementar su escenario con los estándares existentes.
SerialSeb
14
Eso es inteligente, pero demasiado complejo e ineficiente. Ahora tiene que enviar un POST con sus criterios de búsqueda, obtener un URI como respuesta de su POST, luego enviar un GET con el URI de criterios de búsqueda al servidor para que lo obtenga y le devuelva el resultado. (Excepto que incluir un URI en un URI es técnicamente imposible porque no puede enviar algo que puede tener hasta 255 caracteres dentro de algo que no puede tener más de 255 caracteres, por lo que debe usar un identificador parcial y su servidor luego necesita saber cómo resolver el URI para sus criterios de búsqueda PUBLICADOS.)
fijiaaron
30

Puedes enviar un GET con un cuerpo o enviar un POST y renunciar a la religiosidad RESTish (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 una POST puede tener obstáculos con algunos marcos RESTish.

Julian Reschke sugirió anteriormente usar un encabezado HTTP no estándar como "BÚSQUEDA" que podría ser una solución elegante, excepto que es aún menos probable que sea compatible.

Puede ser más productivo enumerar 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 servidores proxy) que despojan a un cuerpo de GET:

  • ?
fijiaaron
fuente
2
Squid 3.1.6 también elimina los cuerpos GET cuando Content-Length es 0 o no está configurado, y de lo contrario envía una HTTP 411 Longitud requerida aunque la longitud esté establecida
rkok
2
Fiddler lo hará, pero te advierte.
toddmo
¿Estás diciendo que un SEARCHmétodo posiblemente se rompería en el camino? Si los proxies no entienden un método, se espera que lo pasen como están, así que no estoy muy seguro de por qué crees que rompería algo ...
Alexis Wilke
22

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

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

RFC 7213 (HTTPbis) establece:

"Una carga útil dentro de un mensaje de solicitud GET no tiene una 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 usarse para afectar el resultado.

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

En resumen, no lo hagas.

Adrien
fuente
21

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

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

Pruébelo usted mismo con un simple netcat:

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

1234

(el contenido de 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 obtienes 400 Bad Request de Bing, Apple, etc. que son atendidos por AkamaiGhost.

Por lo tanto, no recomendaría usar solicitudes GET con una entidad del cuerpo.

usuario941239
fuente
65
Este ejemplo no tiene sentido porque generalmente cuando las personas van a agregar cuerpo a las GETsolicitudes, es porque su propio servidor personalizado puede manejarlo. La pregunta es si las otras "partes móviles" (navegadores, cachés, etc.) funcionarán correctamente.
Pacerier
66
Esta es una solicitud incorrecta porque su carga útil no es esperada (o sensible) para un GET punto final en particular ; no tiene nada que ver con el uso GETen el caso general. Una carga útil aleatoria podría dividirse con la POSTmisma facilidad y devolver la misma 400 Bad Requestsi el contenido no estuviera en un formato que tuviera sentido en el contexto de la solicitud específica.
nobar
Y no solo en ese punto final como un todo, sino más bien en esa URL específica .
Lawrence Dol
1
Esto es irrelevante porque es solo la implementación del servidor de Google en esa URL. Así que no tiene sentido la pregunta
Joel Duckworth
Para mí fue útil, ya que estaba tratando de usar las funciones de Firebase con un get request + body, y este error puede ser muy críptico y difícil de entender.
scrimau
19

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

Un servidor DEBE 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 DEBE ignorarse al manejar la solicitud.

Es decir, los servidores siempre deben leer cualquier cuerpo de solicitud proporcionado de la red (verifique Content-Length o lea un cuerpo fragmentado, etc.). Además, los representantes deben reenviar cualquier cuerpo de solicitud que reciban. Luego, si el RFC define la semántica para el 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 la semántica para el cuerpo, entonces el servidor debería ignorarlo.

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

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 reciba en una solicitud GET.

izrik
fuente
La Sección 9.5 , "POST", tampoco menciona los cuerpos de solicitud, por lo que esta lógica es errónea.
CarLuva
99
@CarLuva La sección POST dice "El método POST se usa para solicitar que el servidor de origen acepte la entidad incluida ..." La sección del cuerpo de la entidad dice "El cuerpo de la entidad se obtiene del cuerpo del mensaje ..." Por lo tanto, el La sección POST menciona el cuerpo del mensaje, aunque indirectamente haciendo referencia al cuerpo de la entidad que lleva el cuerpo del mensaje de la solicitud POST.
frederickf
9

Según XMLHttpRequest, no es válido. De la norma :

4.5.6 El send()método

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 GETo HEAD.

Lanza una InvalidStateErrorexcepción si alguno de los estados no se abre o si send()se establece el indicador.

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

  1. Si el estado no está abierto , arroje una InvalidStateErrorexcepción.
  2. Si send()se establece la bandera, lanzar una InvalidStateErrorexcepción.
  3. Si el método de solicitud es GETo HEAD, establezca el cuerpo en nulo.
  4. Si el cuerpo es nulo, vaya al siguiente paso.

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

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

Rafael Sales
fuente
1
rechazado debido al hecho de que XMLHttpRequest es una implementación. Es posible que no refleje la especificación real que se supone que debe implementar.
floum
10
El voto negativo anterior es incorrecto, si algunas implementaciones no admiten enviar un cuerpo con un GET, entonces esa puede ser una razón para no hacerlo, independientemente de la especificación. En realidad, me encontré con este problema exacto en un producto multiplataforma en el que estoy trabajando, solo la plataforma que usa XMLHttpRequest no pudo enviar el get.
pjcard
8

Si realmente desea enviar un cuerpo JSON / XML almacenable en caché a la aplicación web, el único lugar razonable para colocar sus datos es una cadena de consulta codificada con RFC4648: codificación Base 64 con URL y alfabeto seguro de nombre de archivo . Por supuesto, podría simplemente urlencode JSON y poner en el valor del parámetro de URL, 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 de relleno de Base64 puede ser malo para el valor de parámetro de URL, sin embargo, parece que no; consulte esta discusión: http://mail.python.org/pipermail/python-bugs-list/2007-February/037195.html . Sin embargo, no debe colocar datos codificados sin el nombre de parámetro porque la cadena codificada con relleno se interpretará como clave de parámetro con valor vacío. Me gustaría utilizar algo así como ?_b64=<encodeddata>.

gertas
fuente
Creo que esta es una idea bastante mala :) Pero si tuviera que hacer algo como esto, en su lugar usaría un encabezado HTTP personalizado (y me aseguraría de enviar siempre Vary: en la respuesta).
Evert
Malo o no pero factible :) Con los datos en el encabezado hay un problema similar con el tamaño de los datos, consulte stackoverflow.com/questions/686217/… . Sin embargo, gracias por mencionar el Varyencabezado, no era consciente de su potencial real.
gertas
6

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

cabeza de nube
fuente
5

¿Qué pasa con los encabezados codificados base64 no conformes? "ALGUNOS PARAMS DE APLICACIÓN: sdfSD45fdg45 / aS"

Restricciones de longitud hm. ¿No puedes hacer que tu manejo POST distinga entre los significados? Si desea parámetros simples como la ordenación, no veo por qué esto sería un problema. Supongo que es certeza lo que te preocupa.

chaz
fuente
Puede enviar cualquier parámetro que desee con el x-prefijo, cualquier límite en la longitud de los encabezados sería completamente un límite arbitrario del servidor.
Chris Marisic
4

Estoy molesto porque REST ya que el protocolo no admite OOP y el Getmétodo es una prueba. Como solución, puede serializar su DTO a JSON y luego crear una cadena de consulta. En el lado del servidor, podrá deserializar la cadena de consulta al DTO.

Echa un vistazo a:

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

El marco del servicio web 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<GetClientRequest, ClientResponse>(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
GSerjo
fuente
8
Solo debes usar POST. Si hay un nombre de método en la url, está violando el diseño del resto fundamental. Esto es RPC, usa POST.
Evert
3
No creo que sea un gran problema, tenemos más problemas durante el desarrollo con la URL RESTful (es decir, pedidos / 1). En cuanto a mí, algo está mal con el método Get, es incompatible con OOP. Y a quién le importa cómo se ve la URL :) Pero con un enfoque basado en mensajes podemos crear una interfaz remota estable y es realmente importante. PD: no es RPC, está basado en mensajes
GSerjo
3
Creo que te estás perdiendo todo el punto de REST. Cuando dices, a quién le importa cómo se ve la url, a REST le importa mucho. ¿Y por qué REST sería compatible con OOP?
shmish111
No, no lo hice, solo veo un poco más
GSerjo
4

Por ejemplo, funciona con Curl, Apache y PHP.

Archivo PHP:

<?php
echo $_SERVER['REQUEST_METHOD'] . PHP_EOL;
echo file_get_contents('php://input') . PHP_EOL;

Comando de consola:

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

Salida:

GET
{"the": "body"}
mnv
fuente
¡Experimento divertido! PHP solo leerá $_POSTcuando el cuerpo se envíe con una solicitud POST y application/x-www-form-urlencoded. Eso significa que el cuerpo se ignora en una GETsolicitud. En este caso: $_GETy de $_POSTtodos modos son muy engañosos en este momento. Así que mejor usophp://input
Martin Muzatko
3

En mi humilde opinión, puede enviar el JSONcodificado (es decir encodeURIComponent) en el URL, de esta manera no viola las HTTPespecificaciones y lleva su JSONal servidor.

EthraZa
fuente
28
Sí, pero el problema principal es el límite de longitud, ¿cómo lo manejamos?
Sebas
2

Tiene una lista de opciones que son mucho mejores que usar un cuerpo de solicitud con GET.

Supongamos que tiene categorías y elementos para cada categoría. Ambos serán identificados por una identificación ("catid" / "itemid" por el bien de este ejemplo). Desea ordenar de acuerdo con otro parámetro "sortby" en un "orden" específico. Desea pasar parámetros para "sortby" y "order":

Usted puede:

  1. Use cadenas de consulta, p. Ej. example.com/category/{catid}/item/{itemid}?sortby=itemname&order=asc
  2. Use mod_rewrite (o similar) para las rutas: example.com/category/{catid}/item/{itemid}/{sortby}/{order}
  3. Utilice encabezados HTTP individuales que pase con la solicitud
  4. Utilice un método diferente, por ejemplo, POST, para recuperar un recurso.

Todos tienen sus desventajas, pero son mucho mejores que usar un GET con un cuerpo.

Xenonita
fuente
0

Incluso si una herramienta popular usa esto, como se cita frecuentemente en esta página, creo que sigue siendo una mala idea, ya que es demasiado exótica, a pesar de que no está prohibida por la especificación.

Muchas infraestructuras intermedias pueden simplemente rechazar tales solicitudes.

Por ejemplo, olvidarse de utilizar algunos de los CDN disponible en frente de su sitio web, como este uno :

Si una GETsolicitud del espectador incluye un cuerpo, CloudFront devuelve un código de estado HTTP 403 (Prohibido) al espectador.

Y sí, es posible que las bibliotecas de sus clientes tampoco admitan la emisión de tales solicitudes, como se informa en este comentario .

Frédéric
fuente
0

Estoy usando el RestTemplate de Spring Framework en mi programa cliente y, en el lado del servidor, definí una solicitud GET con un cuerpo Json. Mi propósito principal es el mismo que el suyo: cuando la solicitud tiene numerosos parámetros, ponerlos en el cuerpo parece más ordenado que ponerlos en la cadena de URI prolongada. ¿Si?

Pero, lamentablemente, ¡no está funcionando! El lado del servidor lanzó la siguiente excepción:

org.springframework.http.converter.HttpMessageNotReadableException: falta el cuerpo de solicitud requerido ...

Pero estoy bastante seguro de que el código del cliente proporciona correctamente el cuerpo del mensaje, entonces, ¿qué pasa?

Seguí el método RestTemplate.exchange () y encontré lo siguiente:

// SimpleClientHttpRequestFactory.class
public class SimpleClientHttpRequestFactory implements ClientHttpRequestFactory, AsyncClientHttpRequestFactory {
    ...
    protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
        ...
        if (!"POST".equals(httpMethod) && !"PUT".equals(httpMethod) && !"PATCH".equals(httpMethod) && !"DELETE".equals(httpMethod)) {
            connection.setDoOutput(false);
        } else {
            connection.setDoOutput(true);
        }
        ...
    }
}

// SimpleBufferingClientHttpRequest.class
final class SimpleBufferingClientHttpRequest extends AbstractBufferingClientHttpRequest {
    ...
    protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
        ...
        if (this.connection.getDoOutput() && this.outputStreaming) {
            this.connection.setFixedLengthStreamingMode(bufferedOutput.length);
        }

        this.connection.connect();
        if (this.connection.getDoOutput()) {
            FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream());
        } else {
            this.connection.getResponseCode();
        }
        ...
    }
}

Tenga en cuenta que en el método executeInternal (), el argumento de entrada 'bufferedOutput' contiene el cuerpo del mensaje proporcionado por mi código. Lo vi a través del depurador.

Sin embargo, debido a prepareConnection (), getDoOutput () en executeInternal () siempre devuelve falso, lo que a su vez hace que el bufferedOutput sea completamente ignorado. No se copia en la secuencia de salida.

En consecuencia, mi programa de servidor no recibió el cuerpo del mensaje y lanzó esa excepción.

Este es un ejemplo sobre el RestTemplate del marco Spring. El punto es que, incluso si el cuerpo del mensaje ya no está prohibido por la especificación HTTP, algunas bibliotecas o marcos de clientes o servidores aún pueden cumplir con la especificación anterior y rechazar el cuerpo del mensaje de la solicitud GET.

Zhou
fuente
No estás leyendo las especificaciones o los comentarios aquí correctamente. Los clientes y servidores que sueltan el cuerpo de la solicitud están dentro de las especificaciones. No use los cuerpos de solicitud GET.
Evert
@Evert ¿No leí los comentarios correctamente o tú no? :) Si se desplaza hacia arriba hasta la respuesta de Paul Morgan (la respuesta más importante) y lee los comentarios cuidadosamente, encontrará esto: "El RFC2616 al que se hace referencia como" especificación HTTP / 1.1 "ahora está obsoleto. En 2014 fue reemplazado por RFC 7230-7237. Citar "El cuerpo del mensaje DEBE ser ignorado cuando se maneja la solicitud" ha sido eliminado. Ahora es solo "El encuadre del mensaje de solicitud es independiente de la semántica del método, incluso si el método no define ningún uso para un cuerpo del mensaje ... "
Zhou
@Evert Además, estaba usando la utilidad de prueba REST "tranquilo" para probar mi backend Spring-boot. ¡Tanto el lado del servidor de Spring-boot como el servidor de Json se mantuvieron seguros para solicitar GET! Solo el RestTemplate de Sping-framework elimina el cuerpo de las solicitudes GET, por lo que Spring-boot, rest-sure y RestTemplate, ¿cuál está / está mal?
Zhou
@Evert Último pero no arrendar, no insté a las personas a usar body en las solicitudes GET, por el contrario, sugería que NO lo hiciera analizando el código fuente de RestTemplate de Sping-framework, entonces, ¿por qué votaron en contra de mi ¿responder?
Zhou
No rechacé tu respuesta. Solo estoy aclarando que cualquier implementación HTTP que caiga la solicitud GET está en la especificación.
Evert