http - populares - hashtracking
¿Cuál es la mejor manera de diseñar una solicitud HTTP cuando se necesitan parámetros algo complejos? (11)
Tengo algunos servicios web que estoy escribiendo y trato de ser lo más RESTANTE posible. Estoy hospedando estos servicios web utilizando un HTTPHandler que se ejecuta dentro de IIS / ASP.NET / SharePoint.
La mayoría de mis servicios esperan un HTTP GET. Tengo dos de estos que simplemente devuelven algunos datos (es decir, una consulta) y serán Idempotent , pero los parámetros pueden ser algo complejos. Ambos podrían incluir caracteres en los parámetros del servicio que no están permitidos para al menos la parte PATH de la URL.
Usando IIS, ASP.NET y SharePoint, he encontrado que los siguientes caracteres en la ruta de la URL no llegan a mi HttpHandler incluso si la URL está codificada (la solicitud explota y no tengo ningún control fácil sobre esto) :
- % (% 25)
- & (% 26)
- * (% 2a, pero la url no se codificó)
- + (% 2b)
- : (% 3a)
- <(% 3c)
(% 3e)
Los siguientes caracteres llegaron a mi HttpHandler, pero UriTemplate no pudo manejarlos correctamente incluso si estaba codificado en Url:
(% 23)
- . (% 2e, pero no se codificó la URL; UriTemplate eliminó el "." Si es el último carácter antes de un /)
- ? (% 3f)
- / (% 2f - UriTemplate falla por razones obvias incluso si está codificado en URL)
- / (% 5c)
Por lo tanto, he sido bastante minucioso, pero necesito probar estos caracteres codificados en la url en la cadena de consulta. Parece que esto funcionará en su mayor parte allí.
En uno de mis servicios, los caracteres especiales que son un parámetro son semánticamente parte de una consulta / filtro (en realidad, los términos de búsqueda para un servicio de búsqueda), pero en otro, no son realmente parte de una consulta / filtro, así que idealmente son parte de la ruta y no la cadena de consulta.
Mi pregunta es, ¿qué opción es la mejor? Aquí hay algunos que conozco:
Use HTTP GET y cadena de consulta. Todo lo que pueda usar caracteres especiales debe estar en la cadena de consulta y Url Encoded. Aquí es donde me inclino, pero me preocupan las cadenas de consulta extremadamente largas (IE tiene un límite de 2083)
Use HTTP GET y la codificación base64 dentro de la ruta. Use una Base64 modificada para la URL para cualquier parámetro que pueda usar caracteres especiales y manténgalos como parte de la ruta si lo prefiere. Lo intenté y funciona, pero es feo. Sigue siendo una preocupación sobre cadenas de consulta extremadamente largas.
Use HTTP POST y el cuerpo del mensaje. Cualquier cosa que pueda usar caracteres especiales debe estar en el cuerpo de la solicitud. Parece una solución decente, pero se entiende que las publicaciones no son Idempotent y (pensé) generalmente están destinadas a cambios (mientras que no se producen cambios aquí).
Use HTTP GET y el cuerpo del mensaje. Cualquier cosa que pueda usar caracteres especiales debe estar en el cuerpo de la solicitud. Esto parece una mala idea según SO: HTTP GET con cuerpo de solicitud y Roy Fielding .
Use una combinación de n. ° 3 y n. ° 1 o n. ° 2 arriba, según cuán grande sea la solicitud.
¿¿¿Otro???
Tenga en cuenta que en algunos casos puedo cambiar las cosas para evitar caracteres especiales (y puedo hacerlo), pero no podré hacer esto en todos los casos.
Con respecto a la longitud del URI, RFC2616 Sec3.2.1 dice lo siguiente:
El protocolo HTTP no pone ningún límite a priori en la longitud de un URI. Los servidores DEBEN ser capaces de manejar el URI de cualquier recurso que sirvan, y DEBERÍAN ser capaces de manejar los URI de longitud ilimitada si proporcionan formularios basados en GET que podrían generar dichos URI. Un servidor DEBERÍA devolver el estado 414 (Request-URI Too Long) si un URI es más largo de lo que el servidor puede manejar (ver sección 10.4.15).
Note: Servers ought to be cautious about depending on URI lengths
above 255 bytes, because some older client or proxy
implementations might not properly support these lengths.
Además, la longitud máxima de la URL es de 2.083 caracteres en Internet Explorer .
Relacionados: ¿Cómo pasar consultas complejas en REST?
Apuntaría a HTTP POST. Está muy bien señalizado cuando se trata de PHP (o lo que sea que estés usando) y no tiene los límites de tamaño que tienen los demás.
Base64 debería hacerlo. de lo contrario, use el% -sign que es estándar.
Considere apoyar:
- GET peticiones con cadena de consulta corta
- Solicitudes POST con cadena de consulta larga en el cuerpo y X-HTTP-Method-Override: GET ( https://en.wikipedia.org/wiki/List_of_HTTP_header_fields )
Tenga cuidado al no mezclar "POST / orders" que es una creación masiva de nuevos pedidos y "POST / orders" con un "X-HTTP-Method-Override: GET" que es una búsqueda de orden.
Debe colocar los parámetros en la cadena de consulta, utilizando una solicitud GET de HTTP. Los límites en algunos navegadores web antiguos no son una preocupación, porque las únicas personas que navegan a través de una API en un navegador web probablemente sean desarrolladores (o al menos técnicos).
Recuerde que las aplicaciones cliente no deben manipular las URL que su API les proporciona. Las URL son identificadores opacos para los clientes, utilizados solo para dirigirlos a donde se pueden encontrar recursos particulares.
Si esto no fuera posible por cualquier razón, usaría una solicitud POST con los parámetros codificados en el cuerpo. No será del todo RESTful, pero suponiendo que sus recursos están diseñados correctamente, el impacto en el código del cliente debería ser mínimo.
Definitivamente hubiera empezado donde empezaste: acortamiento de URL. Intentaría acortar los nombres de los parámetros (? A = XXX; b = YYY; c = zzz); Reencadena toda la consulta a Base64; GZip the Base64; Huffman codifica el GZip; ... lo que sea necesario. Una vez que tengo la idea de que el acortamiento no funcionará para todos los casos (tienes algún sistema dinámico de creación de filtro que se puede agregar indefinidamente, o w / e), entonces debes admitir que quizás tratas de hacer todo dentro de una sola solicitud podría no funcionar ...
NO voy a sugerir que arrojes múltiples GET con parámetros divididos e intentes hacer un seguimiento de las solicitudes de esa manera ...
El único método ''robusto'' que PUEDO sugerir es almacenar / establecer la cadena de consulta solicitada en una solicitud (POST) y hacer que devuelva una ID de tamaño fijo (o guid) que identifique la ubicación del parámetro de solicitud en su almacén de datos (filterID). luego realice la solicitud GET real utilizando el token de ID de filtro en lugar del valor de cadena de consulta de filtro completo. Esto permitirá todo tipo de cosas interesantes como las respuestas de caché basadas en filterID para poder (en teoría) reutilizar los mismos filtros más tarde (en lugar de volver a ingresarlos a mano, simplemente guarde una "etiqueta" junto con el cuerpo del filtro y seleccione desde los últimos 5 filtros por etiqueta), o al menos mantenerlos almacenados con sus datos para que cada vez que actualice la página no esté reenviando toda la solicitud de filtro.
Le recomiendo que lea la especificación HTTP 1.1 , especialmente las secciones 3.2 Identificadores de recursos uniformes y 9.1.1 Métodos seguros . Esos esperamos que respondan a su pregunta.
Aquí hay alguna información adicional:
No hay una manera perfecta de hacer esto.
La forma correcta de HTTP / REST sería usar un GET y tener todos sus parámetros en la URL como argumentos de consulta. Has identificado dos problemas prácticos con este enfoque
- El software de su servidor no le está pasando correctamente algunos caracteres, incluso si la URL está codificada. Eso me sorprende, en realidad, y deberías mirar más de cerca lo que está pasando que ni siquiera puedes obtener un% a través de la URL. ¿Tu marco te da acceso sin formato a PATH_INFO o caracteres no procesados? Eso puede darte una solución.
- Sus cadenas de consulta pueden ser demasiado largas. Usted menciona el límite de 2083 bytes en MSIE. Eso puede o no ser un problema práctico para usted, dependiendo de si MSIE es un cliente de su API. (Es decir: a través de Javascript haciendo llamadas a una API JSON). Pero en mi experiencia, las URL muy largas terminarán rompiéndose misteriosamente en varios lugares; proxy almacena en caché a lo largo de la ruta, incluso un firewall con estado. Si tiene control absoluto sobre los clientes y la ruta de la red, probablemente pueda vivir con los peligros de las URL largas. Si se trata de una API pública, olvídate.
Esperemos que pueda hacer que el GET directo funcione en su entorno. Es posible que desee considerar la refacturación de su API para reducir los datos de la consulta.
Pero, ¿y si no puedes hacer que funcione el GET? Propones varias alternativas. Inmediatamente despediría a dos de ellos. No pongas contenido en el cuerpo de solicitud GET; demasiado software se romperá si lo intentas, y de todos modos viola el espíritu REST que intentas capturar. Y no usaría la codificación base64. Puede ayudarlo a solucionar el problema 1, ya que su servidor no maneja correctamente algunos caracteres en las URL. Pero si se aplica incorrectamente hará que sus URL sean más largas, no más cortas, agravando el problema 2. Incluso si hace base64 a la derecha e incluye algo de compresión, las URL no serán mucho más cortas y el cliente será mucho más complicado.
Su solución más práctica es probablemente la opción 3, un HTTP POST. Esto no es RESTful; Debería utilizar GET para consultas de solo lectura. Y perderá algunas ventajas del enfoque REST con el almacenamiento en caché de GET y similares. Por otro lado, funcionará correctamente, y simplemente, con una gran variedad de bibliotecas de software e infraestructura de Internet. A continuación, puede pasar la mayor cantidad de datos que desee en el cuerpo POST, ya sea a través de la codificación multipart / form-data, JSON o XML. (He construido dos servicios web públicos principales utilizando SOAP, que es solo XML en POST. Es feo y no tiene REST, pero funciona de manera confiable).
REST es un gran paradigma de diseño. Es una guía. Si no se ajusta a tu aplicación, no sientas que debes seguir con ella. HTTP no es bueno para pasar grandes cantidades de datos al servidor con un GET. Si necesita tener parámetros de consulta gigantes, haga algo más.
Si está generando estas URL largas en el servidor, puede hacer uso de la compresión para obtener información de la ruta.
Entonces, si tiene algo como /? Param1 = bla-bla & param2 = bla-bla, simplemente comprima esos parámetros y haga que url se vea como /? Query = ASsadcnfAFFASFscnsdlc
Cuando recibe dicha solicitud, simplemente los descomprime y analiza la cadena de parámetros
Si la consulta es demasiado grande para entrar en el URI, conviértala en un recurso (como una búsqueda guardada). Trabajé en una API de descanso para el sistema de reservas de un hotel; la consulta de búsqueda tenía demasiados parámetros (preferencias, lista de habitaciones, etc.), así que lo convertí en un recurso que PUBLICÉ en el servidor. El servidor luego responde con un URI que identifica de forma única la búsqueda, que cuerpo es la consulta publicada + sus resultados:
POST http://hotels.xyz/searches
body <search><query>...</query></search>
Respuesta
201 Created - Location: http://hotels.xyz/searches/someID
Body <search><query>...</query><results>...</results></search>
Use encabezados HTTP personalizados con HTTP GET si nada más funciona. Los encabezados HTTP pueden ser establecidos por casi todos los clientes.
Generalmente es mejor usar los parámetros de URL en la cadena de consulta. Demasiados parámetros de URL indican que necesita dividirse en servicios más finos granulares.
Roy Fielding probablemente aprobaría el uso de POST en esta situación, pero tendrías que preguntarle.
En general, la mayoría de las aplicaciones que involucran datos suministrados por el usuario que se suministran al servidor no son seguras. La única excepción es cuando la información está en la forma de parámetros de consulta generalizados, para los cuales existe un compromiso entre GET y POST que generalmente involucra el tamaño del contenido del parámetro . GET solo es deseable para aquellos casos en que los parámetros pueden expresarse como un URI significativo.