rest - tutorial - HTTP GET con cuerpo de solicitud
rest api tutorial español (17)
Estoy desarrollando un nuevo servicio web RESTful para nuestra aplicación.
Al realizar 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 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 en conjunto?
- ¿Los clientes HTTP tendrán problemas con el uso de cuerpos de solicitud dentro de una solicitud GET?
¿Qué servidor lo ignorará? - fijiaaron agosto 30 ''12 a las 21:27
Por ejemplo, a Google le está yendo peor que ignorarlo, ¡lo considerará un error !
Pruébalo tú mismo con un simple netcat:
$ 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á:
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 recibe 400 solicitudes incorrectas de Bing, Apple, etc ... que son atendidas por AkamaiGhost.
Por lo tanto, no recomendaría el uso de solicitudes GET con una entidad de cuerpo.
¿Qué pasa con los encabezados codificados en base64 no conformes? "SOMETHINGAPP-PARAMS: sdfSD45fdg45 / aS"
Restricciones de longitud hm. ¿No puedes hacer que tu manejo de POST distinga entre los significados? Si desea parámetros simples como la clasificación, no veo por qué esto sería un problema. Supongo que es cierto que estás preocupado.
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 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 desde la red (verifique Content-Length o lea un cuerpo fragmentado, etc.). Además, los apoderados 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 semántica para el cuerpo, entonces el servidor debe 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.
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 clientes (como el controlador de Ruby) pueden registrar el comando cry para salir en modo de desarrollo y usan esta sintaxis ampliamente.
En mi humilde opinión, simplemente puede enviar el encodeURIComponent
JSON
codificado (es decir, encodeURIComponent
) en la URL
, de esta manera no violará las especificaciones de HTTP
y enviará su JSON
al servidor.
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.
Estoy molesto porque REST como 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. En el lado del servidor, podrá 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 ayudarle a resolver la restricción del método Get. Podrás enviar cualquier DTO como con cuerpo de solicitud
El marco de servicios web de Nelibur proporciona una funcionalidad que puede utilizar
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
Hago 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 otra cosa que no sea analizar y descartar ese cuerpo si se recibe"
RFC 7213 (HTTPbis) declara:
"Una carga útil dentro de un mensaje de solicitud GET no tiene una semántica definida";
Ahora parece claro 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 solicitud no puede utilizarse para afectar el resultado.
Hay proxies por ahí que definitivamente romperán tu solicitud de varias maneras si incluyes un cuerpo en GET.
Así que en resumen, no lo hagas.
Lo que estás tratando de lograr se ha hecho durante mucho tiempo con un método mucho más común y que no se basa en usar 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 envíe la solicitud al URI del servidor indicado, diga / busque. El servidor puede generar el resultado de la 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 los resultados, etc.
Dicho esto, los URI están codificados de todas formas para cualquier cosa que no sea ASCII, y también lo son application / x-www-form-urlencoded y multipart / form-data. Recomendaría usar esto en lugar de crear otro formato json personalizado si su intención es admitir escenarios ReSTful.
Ni restclient ni la consola REST son compatibles con esto, pero Curl sí.
La especificación HTTP dice en la sección 4.3
Un cuerpo del 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 una entidad-cuerpo 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 seccion 5.2 dice
El recurso exacto identificado por una solicitud de Internet se determina al examinar tanto el URI de la 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 el URI de solicitud.
Lo que en conjunto sugiere que al procesar una solicitud GET, no se requiere que un servidor examine nada que no sea el campo Request-URI y Host header.
En resumen, la especificación de 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.
No recomendaría esto, va en contra de las prácticas estándar y no ofrece mucho a cambio. Quieres mantener el cuerpo por contenido, no por opciones.
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"}
Puede enviar un GET con un cuerpo o enviar un POST y abandonar la religiosidad RESTish (no es tan malo, hace 5 años solo había un miembro de esa fe: sus comentarios están vinculados más arriba).
Tampoco son decisiones importantes, pero el envío de un cuerpo GET puede evitar problemas para algunos clientes y algunos servidores.
Hacer un POST podría tener obstáculos con algunos marcos RESTish.
Julian Reschke sugirió anteriormente usar un encabezado HTTP no estándar como "SEARCH", que podría ser una solución elegante, excepto que es incluso menos probable que sea compatible.
Podría ser más productivo enumerar los 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 desde GET:
- apache
- PHP
Servidores (y proxies) que le quitan un cuerpo a GET:
- ?
Según XMLHttpRequest, no es válido. De la standard :
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
oHEAD
.Lanza una excepción
InvalidStateError
si alguno de los estados no se abre o si se establece el indicadorsend()
.El método de
send( body )
debe ejecutar estos pasos:
- Si el estado no está abierto , lance una excepción
InvalidStateError
.- Si la
InvalidStateError
send()
está establecida, lanza una excepciónInvalidStateError
.- Si el método de solicitud es
GET
oHEAD
, establezca el cuerpo en nulo.- Si el cuerpo es nulo, vaya al siguiente paso.
Aunque, no creo que deba porque la solicitud GET puede necesitar contenido de cuerpo grande.
Por lo tanto, si confía en XMLHttpRequest de un navegador, es probable que no funcione.
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 mientras que "en su mayoría" se ajustan a la especificación HTTP, lo único que se asegura es que se comportarán como lo usan tradicionalmente los navegadores web. (Estoy pensando en cosas como proxies transparentes, aceleradores, herramientas de A / V, etc.)
Este es el espíritu detrás del Principio de Robustez aproximadamente: "sea liberal en lo que acepta y conservador en lo que envía", no quiere forzar los límites de una especificación sin una buena razón.
Sin embargo, si tienes una buena razón, ve por ello.
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 de Base 64 con URL y Alfabeto de archivo seguro . Por supuesto, usted podría simplemente urlencode JSON y ponerlo en el valor de param 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 =
relleno de Base64 puede ser malo para el valor param de la URL, pero no lo parece. Consulte esta discusión: http://mail.python.org/pipermail/python-bugs-list/2007-February/037195.html . Sin embargo, no debe colocar los datos codificados sin el 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=<encodeddata>
.
El comentario de Roy Fielding acerca de 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 con eso en mente. Sin embargo, la semántica del servidor para GET está restringida de tal manera que un cuerpo, si lo hay, no tiene un significado semántico para la solicitud. Los requisitos en el análisis son independientes de los requisitos en la semántica de métodos.
Entonces, sí, puedes 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á claro una vez que la especificación esté particionada (trabajo en progreso).
.... roy
Sí, puede enviar un cuerpo de solicitud con GET pero no debería tener ningún significado. Si le da un 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 semántica definida para un cuerpo de entidad, entonces el cuerpo del mensaje SHOULD ser ignorado 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.