Requests: Générer des articles en plusieurs parties sans fichier

Créé le 3 janv. 2013  ·  36Commentaires  ·  Source: psf/requests

Actuellement, la seule façon d'avoir une demande de formulaire en plusieurs parties est r = requests.post(url, data=payload, files=files)
qui peut avoir une composante

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

content
--3eeaadbfda0441b8be821bbed2962e4d--

Cependant, je rencontre des cas où les publications doivent être dans un format en plusieurs parties sans fichier associé, comme :

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

value1
--3eeaadbfda0441b8be821bbed2962e4d

mais ce dernier est impossible à générer sans le premier.

Peut-être pouvons-nous ajouter un indicateur comme r = requests.post(url, data=payload, multipart=True) qui force un article à être en plusieurs parties, même sans fichier.

Je suis heureux de travailler à la mise en œuvre de cela si cela semble être une bonne idée.

Tous les 36 commentaires

Cela a déjà été discuté. Cela représenterait un changement important pour l'API que je ne suis pas sûr que @kennethreitz aimerait.

Personnellement, je serais plus en faveur d'exposer une fonction pour générer des données en plusieurs parties à partir de dictionnaires (listes de tuples, etc.) dans l'API afin que les utilisateurs puissent l'utiliser et simplement transmettre les données générées aux requêtes. Apparemment, s'ils n'utilisent pas de fichiers, il ne devrait pas y avoir un énorme coup de mémoire, mais même ainsi, ils ont déjà une énorme chaîne en mémoire, la seconde ne les tuera pas vraiment et ce serait de leur faute, pas de la nôtre .

Peut-être que @kennethreitz serait plus favorable à la deuxième solution. Je ne pense pas non plus que cela corresponde à la philosophie de conception des demandes et serait extraordinairement bizarre compte tenu du reste de l'API, mais _hausse les épaules_ qui sait.

En effet, l'API actuelle ne prend pas en charge l'envoi de données en plusieurs parties autres que des fichiers et c'est une mauvaise chose. Voir le numéro 935 et shazow/urllib3/issues/120

Peut-être qu'il me manque quelque chose, mais les changements me semblent plutôt mineurs. J'ai forké le référentiel et j'ai proposé un changement dans ma version ici : https://github.com/spacecase/requests/commit/45b0b3ce1e76b241b323570a5fc88ae2089c3c3d
(s'il y a une meilleure façon de le faire, faites le moi savoir ? Je suis plutôt nouveau sur github).

Cela pourrait nécessiter quelques tests unitaires et nécessiterait une modification d'une chaîne de documentation dans api.py, mais quelque chose comme ça est-il raisonnable?

Le problème avec le changement n'est pas qu'il est compliqué à mettre en œuvre, c'est qu'il s'agit d'une divergence dans l'API. Je suis un peu sur la clôture dans cette discussion en cours : je pense qu'il serait probablement utile d'avoir un bon moyen de télécharger des données de formulaire en plusieurs parties, mais je pense également que l'API files est très bonne . Je _ne_ pense pas que l'ajout de multipart à l'API Request soit la voie à suivre.

Honnêtement, je préférerais personnellement le contrôler via l'en-tête du type de contenu, mais ce serait probablement 100 fois plus sujet aux erreurs et déroutant pour les nouveaux utilisateurs que ce que nous faisons actuellement. Je suis également sur la clôture à ce sujet, mais je me tromperais toujours de ne pas le faire.

Pourquoi? Si nous acceptons cette demande de fonctionnalité, il devient alors plus probable que quelqu'un se plaigne de ne pas avoir de paramètre json. Et je suis sûr que d'autres personnes pourraient proposer encore plus de paramètres qu'ils aimeraient voir ajoutés. Dans l'état actuel des choses, l'API fait exactement ce qu'elle devrait et n'a que peu ou pas de kruft. Une façon de voir cela est KISS. Cela rend les requêtes et renvoie un excellent objet qui rend l'utilisation de la réponse facile et naturelle. Il fait ce qui est annoncé et vous faites le reste. Il annonce l'encodage en plusieurs parties et le fait grâce à sa conception documentée. Cela peut sembler gênant, mais c'est documenté et cela fonctionne.

_(...) quelqu'un se plaindra de ne pas avoir de paramètre json_

Il n'y aurait aucune raison pour une telle plainte car json est un sous-type du type de média application ( rfc4627 ) et non du type de média multipart ( httpbis draft 21 )

_Ça peut paraître gênant (...)_

C'est gênant alors qu'il ne devrait pas l'être.

Après avoir lu les commentaires précédents, j'aimerais réitérer ma position : multipart/form-data est le type MIME en plusieurs parties le plus couramment utilisé (citation nécessaire :)) et ne pas le prendre en charge est une grave omission.

@piotr-dobrogost: Malgré ce que j'ai dit ci-dessus, je ne trouve même pas l'argument que vous venez de faire un peu convaincant.

Les requêtes ne prennent pas en charge les types MIME, mais les cas d'utilisation. Ce point de vue rend vos deux commentaires ci-dessus étranges. Par exemple, la plainte concernant le fait de ne pas avoir de paramètre JSON sera due au fait que le téléchargement de données au format JSON est très courant - probablement plus courant parmi les utilisateurs de Requests que le téléchargement de données multipartites non-fichier. Affirmer que nous ne le fournirons pas parce que « nous n'avons que des sous-types de cas spéciaux de multipart » semble être une chose étrange à dire.

Quoi qu'il en soit, le cœur du problème est le suivant : l'API est tout l'intérêt de cette bibliothèque. Si vous n'arrivez pas à trouver une _belle_ manière d'implémenter cette fonctionnalité, cela n'arrivera pas.

Il n'y aurait aucun motif pour une telle plainte [sic]

@piotr-dobrogost, vous avez plus d'espoir que moi pour l'avenir des développeurs. Au-delà, le paramètre demandé est inexact. Il faudrait plutôt l'appeler form_data que multipart . Comme vous le notez, (bien qu'indirectement) multipart pourrait faire référence à de nombreux types de médias différents.

C'est gênant alors qu'il ne devrait pas l'être.

Tout ne peut pas être élégant (même en python).

et ne pas le soutenir

Mais il est pris en charge. Mais cela mis à part, nous avons data pour application/x-www-form-urlencoded , files (ou files+data ) pour multipart/form-data , pourquoi avons-nous besoin d'un autre paramètre pour seulement multipart/form-data quand nous l'avons ?

Vous n'avez pas besoin d'utiliser une combinaison de data et files pour obtenir le résultat souhaité. Vous pouvez faire quelque chose comme : requests.post('http://example.com/', files=[('key1', 'param1'), ('key2', 'param2')]) sans y avoir un fichier réel.

Et si nous devions être exacts, form_data ne serait peut-être pas tout à fait évident, alors pourquoi ne pas utiliser le paramètre multipart_form_data , mais maintenant c'est aussi maladroit. C'est explicite, oui, et PEP 8 appelle à l'explicite, mais le comportement existant est bien documenté . Si @kennethreitz décide d'accepter cette demande de fonctionnalité, tout ce que le paramètre devra faire est d'agir comme un alias du paramètre files. Mais étant donné que le comportement est déjà pris en charge, je ne pense pas que ce soit nécessaire.

@sigmavirus24 et @Lukasa ont parfaitement résumé cela.

@piotr-dobrogost vos contributions sont appréciées, mais pas votre ton. S'il vous plaît, arrêtez de faire des affirmations fermes contre notre projet et ses objectifs.

De mon point de vue, il est assez frustrant que les demandes prennent déjà en charge les publications en plusieurs parties, mais ne permettent pas à l'utilisateur d'y accéder sans utiliser de fichier. La suggestion de @ sigmavirus24 de simplement y coller un fichier n'est pas adéquate. Les applications Web avec lesquelles je travaille renverront des codes d'erreur si quelque chose comme cela est essayé.

Je soupçonne que cela continuera à être un problème pour les utilisateurs à l'avenir, et je suis un peu confus pourquoi il ne semble pas y avoir d'effort pour résoudre ce problème.

Voir ma réponse au #935

Oh merci. J'attends cela avec impatience!

@Lukasa

_Requests ne prend pas en charge les types MIME, il prend en charge les cas d'utilisation._

L'envoi de données multipart/form-data est un cas d'utilisation courant.

_(...) le téléchargement de données au format JSON est très courant (...)_

Nous ne pouvons pas comparer l'envoi de json avec l'envoi de multipart/form-data . L'envoi de json est facile ; vous définissez Content-type , encodez les données sur une ligne à l'aide du module intégré et c'est tout. L'envoi de multipart/form-data est plus complexe car le corps de la requête doit avoir une structure spécifique. En d'autres termes, l'envoi de json est en quelque sorte transparent en ce qui concerne HTTP, mais l'envoi de multipart/form-data ne l'est pas. Comme Requests est la bibliothèque HTTP, elle devrait s'occuper de créer une telle structure.

_Si vous n'arrivez pas à trouver une belle façon d'implémenter cette fonctionnalité, cela n'arrivera pas._

Avoir le paramètre actuel de files pour envoyer multipart/form-data qui n'a RIEN à voir avec des fichiers n'est pas beau du tout (c'est moche), pourtant il a réussi à entrer dans la base de code :). Trouver quelque chose de moins moche est vraiment facile :)

@sigmavirus24

_(...) mais le comportement existant est bien documenté._

Aucune quantité de documentation ne fait une bonne API d'une mauvaise API. Plus l'API est bonne, moins elle a besoin de documentation.

_Vous pouvez faire quelque chose comme (...)_

C'est vrai, mais c'est très trompeur et peu intuitif. J'ai décrit ce qui ne va pas avec l'utilisation du paramètre files pour cela dans mon commentaire précédent .

Conclusion : pour trouver quelque chose de mieux, nous devons admettre que l'API actuelle est mauvaise en ce qui concerne l'envoi de multipart/form-data données.

@spacecase

_De mon point de vue, il est assez frustrant que les demandes prennent déjà en charge les publications en plusieurs parties, mais ne permettent pas à l'utilisateur d'y accéder sans utiliser de fichier_

Je suis d'accord et c'est pourquoi j'ai créé le numéro 935.

Ce problème est clos.

Pardonnez-moi @kennethreitz mais @spacecase Je n'ai utilisé de fichier nulle part dans cet exemple. J'ai utilisé le paramètre files pour faire exactement ce que vous voulez. Si j'avais utilisé un fichier, vous auriez vu open('filename') .

@sigmavirus24 , Malheureusement, ce n'est pas ce que je veux. Il ne s'agit pas de savoir si le client lit un fichier ou non. C'est ce qui est dit au serveur. Dans votre exemple, le corps du message est

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--

Notez l'utilisation de filename= . Il indique au serveur qu'un fichier est envoyé. Dans les applications Web avec lesquelles je travaille, ce comportement incorrect entraîne une erreur.

Je suis désolé, mais que tu sois si présomptueux de me dire que c'est exactement ce que je veux m'offense. Je suis venu offrir des idées et de l'aide, et il semble que vous n'en vouliez pas non plus dans ce cas. C'est bien, mais s'il vous plaît, ne me faites pas sentir qu'on me rabaisse.

Hm, il semble que je me souvienne mal du comportement. Désolé pour ça. Ce n'était pas mon intention de t'offenser du tout ou de te rabaisser. C'est certainement suffisant pour me pousser à avoir besoin d'une meilleure façon de gérer multipart/form-data , mais pour l'instant, je ne suis toujours pas d'accord pour dire que les idées pour l'API ne sont pas du tout élégantes.

@spacecase s'il vous plaît voir ma réponse au #935. Les choses iront mieux. Vos commentaires sont importants et très appréciés. :)

@spacecase comme un geste pour montrer que votre opinion compte, consultez sigmavirus24/requests-data-schemes comme mesure provisoire. Vous devrez définir votre propre en-tête Content-Type , mais cela peut être un inconvénient mineur.

Merci @sigmavirus24 , je vais l'essayer. Il semble que cela fera l'affaire.
J'apprécie que vous ne vouliez pas m'offenser. Je me sens mieux avec ça et je n'ai rien contre toi ou le projet.

Oui, il semble que je sois impétueux pour certaines personnes, alors je suppose que je dois affiner ce que j'écris sur Internet. Je ne le comprends pas et d'autres sont d'accord avec moi, mais j'y travaille. Je suppose que j'ai juste besoin d'un échantillon suffisamment grand pour réaliser ce qui est offensant pour les gens sans que je le veuille (en plus de me ridiculiser en me souvenant de quelque chose qui fonctionne d'une manière qui ne l'est pas).

J'ai un fichier et des valeurs-clés à publier. Alors, quelle est la bonne façon d'envoyer une telle requête multipartite ? J'espère que vous pouvez m'aider.

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 pour les questions, veuillez utiliser StackOverflow . Pour répondre à votre question cependant, il y a un projet en cours pour résoudre ce problème sigmavirus24/requests-toolbelt

Notez également que j'ai déjà répondu à cette question sur StackOverflow, comme vous pouvez le voir ici .

J'ai le même problème de génération de publications en plusieurs parties sans fichier. Pour le moment, cela ne fait aucune différence si vous faites requests.post(url, data=data_dict) ou requests.post(url, data=data_dict, files={}) . Mais, puisque le mot-clé files défaut est None , nous devrions pouvoir avoir deux comportements distincts. Un multipart/form-data lorsque files={} est spécifié, et application/x-www-form-urlencoded cas contraire.

Ai-je oublié quelque chose?

@jwoillez Avez-vous suivi le lien de débordement de pile que j'ai posté?

Je pense que oui, mais votre réponse là-bas traite de Multipart POST avec un fichier. Je suis revenu au problème d'origine : POST en plusieurs parties sans fichier.

L'objet fichier peut être une chaîne. =) Cela devrait vous fournir les informations que vous vouliez.

Mais si je suis votre suggestion, est-ce que je ne finirai pas avec quelque chose comme ça :

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

content
--3eeaadbfda0441b8be821bbed2962e4d--

content est la chaîne que vous m'invitez à utiliser à la place du fichier ?

Je suis vraiment après ça seulement :

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

value1
--3eeaadbfda0441b8be821bbed2962e4d

Les champs du tuple que vous ne voulez pas peuvent être laissés par défaut :

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

À l'avenir, veuillez adresser vos questions à StackOverflow. Tous les mainteneurs en gardent une trace régulièrement, et c'est l'endroit le plus approprié pour poser ces questions.

C'est la réponse que je cherchais, merci. Désolé pour le bruit.

Peut-être une dernière question, est-ce que ce qui suit est possible (nom de fichier vide spécifié, contenu vide) ?

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


--3eeaadbfda0441b8be821bbed2962e4d--

Vous pouvez fournir un contenu vide en utilisant une chaîne vide dans la section de contenu du tuple. Vous ne pouvez pas fournir un nom de fichier vide littéral, mais un nom de fichier non présent doit être traité exactement de la même manière.

J'ai parfois eu besoin de le faire pour diverses raisons étranges.
Je suggérerais cette approche à tous ceux qui souhaitent renforcer l'API dans ce cas d'utilisation :

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)

Ou vous pouvez utiliser la ceinture à outils et ne pas recourir à des hacks.

Cette page vous a été utile?
0 / 5 - 0 notes