Requests: Genere publicaciones de varias partes sin un archivo

Creado en 3 ene. 2013  ·  36Comentarios  ·  Fuente: psf/requests

Actualmente, la única forma de tener una solicitud de formulario de varias partes es r = requests.post(url, data=payload, files=files)
que puede tener un componente

Content-Disposition: form-data; name="file"; filename="filename.txt"
Content-Type: text/plain

content
--3eeaadbfda0441b8be821bbed2962e4d--

Sin embargo, me encuentro con casos en los que se requiere que las publicaciones estén en un formato de varias partes sin un archivo asociado, como:

Content-Disposition: form-data; name="key1"

value1
--3eeaadbfda0441b8be821bbed2962e4d

pero el segundo es imposible de generar sin el primero.

Quizás podamos agregar una bandera como r = requests.post(url, data=payload, multipart=True) que obligue a una publicación a ser multiparte, incluso sin un archivo.

Estoy feliz de trabajar en la implementación de esto si parece una buena idea.

Todos 36 comentarios

Esto se ha discutido antes. Representaría un cambio significativo en la API que no estoy seguro de que le guste a @kennethreitz .

Personalmente, estaría más a favor de exponer una función para generar datos de varias partes a partir de diccionarios (listas de tuplas, etc.) en la API para que los usuarios puedan usar eso y simplemente pasar los datos generados a las solicitudes. Aparentemente, si no están usando archivos, no debería haber un gran golpe de memoria, pero aún así, ya tienen una cadena enorme en la memoria, la segunda realmente no los matará y sería su culpa, no la nuestra. .

Quizás @kennethreitz estaría más dispuesto a

De hecho, la API actual no admite el envío de datos de varias partes que no sean archivos y esto es algo malo. Consulte el número 935 y shazow / urllib3 / issues / 120

Tal vez me esté perdiendo algo, pero los cambios me parecen bastante menores. Bifuré el repositorio y tengo un cambio propuesto en mi versión aquí: https://github.com/spacecase/requests/commit/45b0b3ce1e76b241b323570a5fc88ae2089c3c3d
(Si hay una mejor manera de hacer esto, avíseme. Soy bastante nuevo en github).

Podría necesitar un par de pruebas unitarias y necesitaría un cambio en una cadena de documentos en api.py, pero ¿es algo como esto razonable?

El problema con el cambio no es que sea complicado de implementar, es que es una divergencia en la API. Estoy un poco indeciso en esta discusión en curso: creo que probablemente sería útil tener una buena manera de cargar datos de formularios de varias partes, pero también creo que la API actual de files es muy buena . Sin embargo, _no_ creo que agregar multipart a la API Request es el camino a seguir.

Honestamente, personalmente preferiría controlarlo a través del encabezado del tipo de contenido, pero eso probablemente sería 100 veces más propenso a errores y confuso para los nuevos usuarios que lo que hacemos actualmente. Estoy en la cerca sobre esto también, pero todavía me equivocaría por no hacer esto.

¿Por qué? Si aceptamos esta solicitud de función, es más probable que alguien se queje de no tener un parámetro json. Y estoy seguro de que a otras personas se les podrían ocurrir aún más parámetros que les encantaría ver agregados. Como está ahora, la API hace exactamente lo que debería y tiene poco o ningún kruft. Una forma de ver esto es KISS. Esto hace que las solicitudes y las devoluciones sean un gran objeto que hace que el uso de la respuesta sea fácil y natural. Hace lo que se anuncia y tú haces el resto. Hace publicidad de la codificación multiparte y lo hace a través de su diseño documentado. Puede parecer extraño, pero está documentado y funciona.

_ (...) alguien se quejará de no tener un parámetro json_

No habría motivo para una queja de este tipo porque json es un subtipo de application tipo de medio ( rfc4627 ) no multipart tipo de medio ( httpbis borrador 21 )

_Puede parecer incómodo (...) _

Es incómodo cuando no debería serlo.

Después de leer los comentarios anteriores, me gustaría reiterar mi posición: multipart/form-data es el tipo MIME multiparte más común que se usa actualmente (se necesita una cita :)) y no admitirlo es una omisión grave.

@ piotr-dobrogost: A pesar de lo que dije anteriormente, no encuentro el argumento que acaba de presentar ni siquiera un poco convincente.

Las solicitudes no son compatibles con los tipos MIME, son compatibles con los casos de uso. Este punto de vista hace que los dos comentarios anteriores parezcan extraños. Por ejemplo, la queja sobre no tener un parámetro JSON se debe a que la carga de datos con formato JSON es muy común, probablemente más común entre los usuarios de Solicitudes que la carga de datos de varias partes que no son archivos. Argumentar que no lo proporcionaremos porque 'solo tenemos subtipos de casos especiales de multipart ' parece algo extraño de decir.

Independientemente, el núcleo del problema es este: la API es el objetivo de esta biblioteca. Si no puede encontrar una forma _hermosa_ de implementar esta funcionalidad, no sucederá.

No habría fundamento para tal queja [sic]

@ piotr-dobrogost tienes más esperanzas para el futuro de los desarrolladores que yo. Más allá de eso, el parámetro solicitado es inexacto. Debería llamarse algo más parecido a form_data que multipart . Como puede observar, (aunque indirectamente) multipart podría referirse a muchos tipos de medios diferentes.

Es incómodo cuando no debería serlo.

No todas las cosas pueden ser elegantes (incluso en Python).

y no apoyarlo

Pero es compatible. Pero aparte de eso, tenemos data por application/x-www-form-urlencoded , files (o files+data ) por multipart/form-data , ¿por qué necesitamos otro parámetro? por solo multipart/form-data cuando lo tengamos?

No es necesario utilizar una combinación de data y files para obtener el resultado deseado. Puede hacer algo parecido a: requests.post('http://example.com/', files=[('key1', 'param1'), ('key2', 'param2')]) sin tener un archivo real allí.

Y si fuéramos exactos, form_data podría no ser del todo obvio, así que ¿por qué no usar el parámetro multipart_form_data , pero eso también es torpe? Es explícito, sí, y PEP 8 pide explícitamente, pero el comportamiento existente está bien documentado . Si @kennethreitz decide aceptar esta solicitud de función, todo lo que tendrá que hacer el parámetro es actuar como un alias para el parámetro de archivos. Pero considerando que el comportamiento ya está soportado , no creo que sea necesario.

@ sigmavirus24 y @Lukasa resumieron esto perfectamente.

@ piotr-dobrogost se agradecen sus contribuciones, pero no su tono. Deje de hacer afirmaciones firmes contra nuestro proyecto y sus objetivos.

Desde mi perspectiva, es bastante frustrante que las solicitudes ya tengan soporte para publicaciones de varias partes, pero no le dan al usuario acceso a eso sin usar un archivo. La sugerencia de @ sigmavirus24 de simplemente pegar un archivo allí no es adecuada. Las aplicaciones web con las que trabajo devolverán códigos de error si se intenta algo como esto.

Sospecho que esto seguirá siendo un problema para los usuarios en el futuro, y estoy un poco confundido por qué no parece haber un esfuerzo para solucionarlo.

Vea mi respuesta al # 935

Oh gracias. ¡Espero que!

@Lukasa

_Requests no admite tipos MIME, admite casos de uso_

Enviar multipart/form-data datos es un caso de uso común.

_ (...) subir datos en formato JSON es muy común (...) _

No podemos comparar el envío de json con el envío de multipart/form-data . Enviar json es fácil; configura Content-type , codifica datos en una línea usando el módulo incorporado y eso es todo. Enviar multipart/form-data es más complejo porque el cuerpo de la solicitud debe tener una estructura específica. En otras palabras, enviar json es algo transparente en lo que respecta a HTTP, pero enviar multipart/form-data no lo es. Como Requests es la biblioteca HTTP, debería encargarse de crear dicha estructura.

_Si no se le ocurre una manera hermosa de implementar esta funcionalidad, no sucederá.

Tener actual files param para enviar multipart/form-data que no tiene NADA que ver con archivos no es hermoso de ninguna manera (es feo), sin embargo, de alguna manera logró ingresar al código base :). Crear algo menos feo es realmente fácil :)

@ sigmavirus24

_ (...) pero el comportamiento existente está bien documentado.

Ninguna cantidad de documentación hace una buena API a partir de una mala API. Cuanto mejor sea la API, menos documentación necesitará.

_Puedes hacer algo parecido a (...) _

Correcto, pero esto es muy engañoso y poco intuitivo. Describí lo que está mal con el uso de files param para esto en mi comentario anterior .

En pocas palabras: para encontrar algo mejor, debemos admitir que la API actual es mala con respecto al envío de datos multipart/form-data .

@spacecase

_Desde mi perspectiva, es bastante frustrante que las solicitudes ya tengan soporte para publicaciones de varias partes, pero no le dan al usuario acceso a eso sin usar un archivo_

Estoy de acuerdo y por eso creé el número 935.

Este problema está cerrado.

Perdóname @kennethreitz pero @spacecase no open('filename') .

@ sigmavirus24 , Desafortunadamente eso no es lo que quiero. No se trata de si el cliente está leyendo un archivo o no. Es lo que se le dice al servidor. En su ejemplo, el cuerpo de la publicación es

Content-Disposition: form-data; name="key1"; filename="key1"
Content-Type: application/octet-stream

param1
--2f8732ee35564115a6c6e0c1032773e8
Content-Disposition: form-data; name="key2"; filename="key2"
Content-Type: application/octet-stream

param2
--2f8732ee35564115a6c6e0c1032773e8--

Tenga en cuenta el uso de filename= . Le dice al servidor que se está enviando un archivo. En las aplicaciones web que trabajo con este comportamiento incorrecto se produce un error.

Lo siento, pero que seas tan presuntuoso y me digas que eso es exactamente lo que quiero, me ofende. Vine para ofrecer ideas y ayudar, y parece que tú tampoco quieres en este caso. Eso está bien, pero por favor no me hagas sentir que me menosprecian.

Hm, parece que recordé el comportamiento incorrectamente. Lo siento por eso. No era mi intención ofenderte en absoluto o hacerte sentir menospreciado. Eso definitivamente es suficiente para hacerme inclinarme a necesitar una mejor manera de manejar multipart/form-data , pero por ahora sigo en desacuerdo con que las ideas para la API no son nada elegantes.

@spacecase, por favor vea mi respuesta al # 935. Las cosas irán mejor. Sus comentarios son importantes y muy apreciados. :)

@spacecase como un gesto para mostrar que su opinión es importante, consulte sigmavirus24 / request-data-esquemas como medida provisional. Tendrá que configurar su propio encabezado Content-Type , pero eso puede ser una molestia menor.

Gracias @ sigmavirus24 , lo probaré. Parece que funcionará.
Aprecio que no quisiste ofenderme. Me siento mejor con eso y no tengo nada en contra de ti ni del proyecto.

Sí, parece que algunas personas me muestran descarado, así que supongo que necesito refinar lo que escribo en Internet. No lo entiendo y otros han estado de acuerdo conmigo, pero estoy trabajando en eso. Supongo que solo necesito un tamaño de muestra lo suficientemente grande como para darme cuenta de lo que es ofensivo para las personas sin mi intención (además de hacerme el ridículo al recordar que algo funciona de una manera que no es así).

Tengo un archivo y algunos valores-clave para publicar. Entonces, ¿cuál es la forma correcta de enviar una solicitud de formulario múltiple? Espero que puedas ayudarme.

Content-Disposition: form-data; name="up"; filename="aa.PNG"
Content-Type: image/png

file data
---------------------------7dee5302248e
Content-Disposition: form-data; name="exp"


-----------------------------7dee5302248e
Content-Disposition: form-data; name="ptext"

text
-----------------------------7dee5302248e
Content-Disposition: form-data; name="board"

DV_Studio
-----------------------------7dee5302248e--

I tried this way but only got a 504 error.
myfile=[('file',open('bb.jpg')),('exp','python'),('ptext',''),('board','DV_Studio')]
r = requests.post(url,files=myfile)

@deerstalker para preguntas, use StackOverflow . Sin embargo, para responder a su pregunta, hay un proyecto en marcha para abordar este problema sigmavirus24 / request-toolbelt

También tenga en cuenta que he respondido esta pregunta antes en StackOverflow, como puede ver aquí .

Tengo el mismo problema de generar publicaciones de varias partes sin un archivo. Por el momento, no importa si hace requests.post(url, data=data_dict) o requests.post(url, data=data_dict, files={}) . Pero, dado que la palabra clave files predeterminada es None , deberíamos poder tener dos comportamientos distintos. A multipart/form-data cuando se especifica files={} y application/x-www-form-urlencoded cuando no.

¿Me he perdido algo?

@jwoillez ¿ publiqué ?

Creo que sí, pero su respuesta allí trata sobre POST de varias partes con un archivo. Volví al problema original: POST de varias partes sin archivo.

El objeto de archivo puede ser una cadena. =) Eso debería proporcionarle la información que deseaba.

Pero si sigo tu sugerencia, ¿no terminaré con algo como esto?

Content-Disposition: form-data; name="file"; filename="filename.txt"
Content-Type: text/plain

content
--3eeaadbfda0441b8be821bbed2962e4d--

donde content es la cadena que me invitas a usar en lugar del archivo?

Realmente solo busco esto:

Content-Disposition: form-data; name="key1"

value1
--3eeaadbfda0441b8be821bbed2962e4d

Los campos de la tupla que no desea se pueden dejar como predeterminados:

files = {'name': ('', 'content')}

En el futuro, dirija sus preguntas a StackOverflow. Todos los encargados del mantenimiento lo controlan con regularidad, y es el lugar más apropiado para hacer estas preguntas.

Esa es la respuesta que estaba buscando, gracias. Perdón por el ruido.

Tal vez una última pregunta, ¿es posible lo siguiente (nombre de archivo vacío especificado, contenido vacío)?

Content-Disposition: form-data; name="file"; filename=""
Content-Type: text/plain


--3eeaadbfda0441b8be821bbed2962e4d--

Puede proporcionar un contenido vacío utilizando una cadena vacía en la sección de contenido de la tupla. No puede proporcionar un nombre de archivo literalmente vacío, pero un nombre de archivo no presente debe tratarse exactamente de la misma manera.

Ocasionalmente he tenido que hacer esto por varias razones extrañas.
Sugeriría este enfoque para cualquiera que desee reforzar la API en este caso de uso:

class ForceMultipartDict(dict):
    def __bool__(self):
        return True


FORCE_MULTIPART = ForceMultipartDict()  # An empty dict that boolean-evaluates as `True`.


client.post("/", data={"some": "data"}, files=FORCE_MULTIPART)

O puede usar el cinturón de herramientas y no recurrir a hacks.

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

iLaus picture iLaus  ·  3Comentarios

jakul picture jakul  ·  3Comentarios

ReimarBauer picture ReimarBauer  ·  4Comentarios

avinassh picture avinassh  ·  4Comentarios

eromoe picture eromoe  ·  3Comentarios