Requests: Generiere mehrteilige Beiträge ohne Datei

Erstellt am 3. Jan. 2013  ·  36Kommentare  ·  Quelle: psf/requests

Derzeit ist die einzige Möglichkeit für eine mehrteilige Formularanforderung r = requests.post(url, data=payload, files=files)
die eine Komponente haben können

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

content
--3eeaadbfda0441b8be821bbed2962e4d--

Ich stoße jedoch auf Fälle, in denen Beiträge in einem mehrteiligen Format ohne zugehörige Datei vorliegen müssen, wie zum Beispiel:

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

value1
--3eeaadbfda0441b8be821bbed2962e4d

aber letzteres ist ohne ersteres nicht zu erzeugen.

Vielleicht können wir ein Flag wie r = requests.post(url, data=payload, multipart=True) hinzufügen, das erzwingt, dass ein Beitrag auch ohne Datei mehrteilig ist.

Ich arbeite gerne an der Umsetzung, wenn es sich nach einer guten Idee anhört.

Alle 36 Kommentare

Dies wurde bereits diskutiert. Es würde eine signifikante Änderung an der API darstellen, von der ich nicht sicher bin, ob @kennethreitz dies möchte.

Persönlich wäre ich eher dafür, eine Funktion zum Generieren mehrteiliger Daten aus Wörterbüchern (Listen von Tupeln usw.) in der API bereitzustellen, damit Benutzer diese verwenden und die generierten Daten einfach an Anfragen übergeben können. Angeblich, wenn sie keine Dateien verwenden, sollte es keinen großen Speichertreffer geben, aber trotzdem haben sie bereits eine riesige Zeichenfolge im Speicher, die zweite wird sie nicht wirklich töten und es wäre ihre Schuld, nicht unsere .

Vielleicht wäre @kennethreitz für die zweite Lösung zugänglicher. Ich denke, es passt auch nicht zur Designphilosophie von Requests und wäre angesichts des Rests der API außerordentlich bizarr, aber _mit den Schultern_, wer weiß.

Tatsächlich unterstützt die aktuelle API das Senden von mehrteiligen Daten außer Dateien nicht, und dies ist eine schlechte Sache. Siehe Ausgabe #935 und shazow/urllib3/issues/120

Vielleicht übersehe ich etwas, aber die Änderungen erscheinen mir eher geringfügig. Ich habe das Repository gegabelt und habe hier eine vorgeschlagene Änderung in meiner Version: https://github.com/spacecase/requests/commit/45b0b3ce1e76b241b323570a5fc88ae2089c3c3d
(Wenn es einen besseren Weg gibt, dies zu tun, lassen Sie es mich wissen? Ich bin ziemlich neu bei Github).

Es könnte ein paar Unittests erfordern und eine Änderung eines Docstrings in api.py erfordern, aber ist so etwas vernünftig?

Das Problem mit der Änderung ist nicht, dass sie kompliziert zu implementieren ist, sondern dass es sich um eine Divergenz in der API handelt. Ich bin in dieser laufenden Diskussion ziemlich am Zaun: Ich denke, es wäre wahrscheinlich nützlich, eine gute Möglichkeit zu haben, mehrteilige Formulardaten hochzuladen, aber ich denke auch, dass die aktuelle files API eine sehr gute ist . Ich glaube aber nicht, dass das Hinzufügen von multipart zur Request API der richtige Weg ist.

Ehrlich gesagt würde ich es persönlich lieber über den Inhaltstyp-Header steuern, aber das wäre wahrscheinlich 100-mal fehleranfälliger und für neue Benutzer verwirrend als das, was wir derzeit tun. Ich bin auch diesbezüglich am Zaun, aber ich würde immer noch auf der Seite irren, dies nicht zu tun.

Wieso den? Wenn wir diese Funktionsanfrage annehmen, wird es wahrscheinlicher, dass sich jemand darüber beschwert, keinen json-Parameter zu haben. Und ich bin mir sicher, dass sich andere Leute noch mehr Parameter einfallen lassen könnten, die sie gerne hinzugefügt sehen würden. So wie es jetzt ist, macht die API genau das, was sie soll und hat wenig bis gar keine Kruft. Eine Möglichkeit, dies zu betrachten, ist KISS. Dies macht die Anfragen und Rückgaben zu einem großartigen Objekt, das die Verwendung der Antwort einfach und natürlich macht. Es tut, was beworben wird und Sie tun den Rest. Es wirbt für mehrteilige Codierung und tut dies durch sein dokumentiertes Design. Es mag umständlich aussehen, aber es ist dokumentiert und es funktioniert.

_(...) jemand wird sich darüber beschweren, dass er keinen Json-Parameter hat_

Es gibt keinen Grund für eine solche Beschwerde, da json ein Untertyp des Medientyps application ( rfc4627 ) und nicht des multipart ( httpbis Draft 21 ) ist.

_Es sieht vielleicht unangenehm aus (...)_

Es ist unangenehm, obwohl es nicht sein sollte.

Nachdem ich die vorherigen Kommentare gelesen habe, möchte ich meine Position wiederholen: multipart/form-data ist der derzeit am häufigsten verwendete mehrteilige MIME-Typ (Zitat erforderlich :)) und es ist eine grobe Unterlassung, ihn nicht zu unterstützen.

@piotr-dobrogost: Trotz allem, was ich oben gesagt habe, finde ich das Argument, das Sie gerade vorgebracht haben, nicht einmal ein wenig überzeugend.

Requests unterstützt keine MIME-Typen, sondern Anwendungsfälle. Dieser Standpunkt lässt Ihre beiden obigen Kommentare seltsam erscheinen. Die Beschwerde über das Fehlen eines JSON-Parameters besteht beispielsweise darin, dass das Hochladen von JSON-formatierten Daten sehr verbreitet ist - wahrscheinlich häufiger bei Benutzern von Anfragen als das Hochladen von nicht dateibasierten mehrteiligen Daten. Zu argumentieren, dass wir es nicht bereitstellen würden, weil 'wir nur Subtypen von multipart Sonderfällen vorgeben', scheint eine bizarre Aussage zu sein.

Unabhängig davon ist der Kern des Problems Folgendes: Die API ist der springende Punkt dieser Bibliothek. Wenn Sie keine _schöne_ Möglichkeit finden, diese Funktionalität zu implementieren, wird es nicht passieren.

Es gäbe keinen Grund für eine solche Beschwerde

@piotr-dobrogost du hast mehr Hoffnung für die Zukunft der Entwickler als ich. Darüber hinaus ist der angeforderte Parameter ungenau. Es müsste eher form_data als multipart heißen. Wie Sie bemerken, kann sich multipart (wenn auch indirekt) auf viele verschiedene Medientypen beziehen.

Es ist unangenehm, obwohl es nicht sein sollte.

Nicht alle Dinge können elegant sein (auch in Python).

und nicht unterstützen

Aber es wird unterstützt. Aber davon abgesehen haben wir data für application/x-www-form-urlencoded , files (oder files+data ) für multipart/form-data , warum brauchen wir noch einen weiteren Parameter? für nur multipart/form-data wenn wir es haben?

Sie müssen keine Kombination aus data und files , um das gewünschte Ergebnis zu erzielen. Sie können Folgendes tun: requests.post('http://example.com/', files=[('key1', 'param1'), ('key2', 'param2')]) ohne eine tatsächliche Datei dort zu haben.

Und wenn wir genau sein wollten, wäre form_data vielleicht nicht ganz offensichtlich, warum also nicht den Parameter multipart_form_data , aber das ist jetzt auch ungeschickt. Es ist explizit, ja, und PEP 8 fordert explizit, aber das vorhandene Verhalten ist gut dokumentiert . Wenn @kennethreitz sich entschließt, diese Funktionsanforderung zu akzeptieren, muss der Parameter Dateiparameter fungieren. Aber wenn man bedenkt, dass das Verhalten bereits unterstützt wird , halte ich es nicht für notwendig.

@sigmavirus24 und @Lukasa haben das perfekt auf den Punkt gebracht.

@piotr-dobrogost Ihre Beiträge werden geschätzt, aber nicht Ihr Ton. Bitte hören Sie auf, feste Behauptungen gegen unser Projekt und seine Ziele aufzustellen.

Aus meiner Sicht ist es ziemlich frustrierend, dass Requests bereits Unterstützung für mehrteilige Posts haben, dem Benutzer jedoch keinen Zugriff darauf ohne Verwendung einer Datei geben. Der Vorschlag von einzufügen , ist nicht ausreichend. Die Webanwendungen, mit denen ich arbeite, geben Fehlercodes zurück, wenn so etwas versucht wird.

Ich vermute, dass dies auch in Zukunft ein Problem für Benutzer sein wird, und ich bin ein wenig verwirrt, warum es keine Bemühungen zu geben scheint, dies zu beheben.

Siehe meine Antwort zu #935

Oh Danke. Ich freue mich darauf!

@Lukasa

_Requests unterstützt keine MIME-Typen, es unterstützt Anwendungsfälle._

Das Senden von multipart/form-data Daten ist ein häufiger Anwendungsfall.

_(...) das Hochladen von JSON-formatierten Daten ist sehr verbreitet (...)_

Wir können das Senden von Json nicht mit dem Senden von multipart/form-data . Das Senden von Json ist einfach; Sie setzen Content-type , codieren Daten in einer Zeile mit dem eingebauten Modul und das war's. Das Senden von multipart/form-data ist komplexer, da der Hauptteil der Anfrage eine bestimmte Struktur haben muss. Mit anderen Worten, das Senden von Json ist in Bezug auf HTTP irgendwie transparent, aber das Senden von multipart/form-data ist es nicht. Da Requests die HTTP-Bibliothek ist, sollte sie sich um die Erstellung einer solchen Struktur kümmern.

_Wenn Sie keine schöne Möglichkeit finden, diese Funktionalität zu implementieren, wird es nicht passieren._

Aktuelle files param zu haben, um multipart/form-data zu senden, was NICHTS mit Dateien zu tun hat, ist in keiner Weise schön (es ist hässlich), aber es hat es irgendwie geschafft, in die Codebasis zu gelangen :). Etwas weniger Hässliches zu finden ist wirklich einfach :)

@sigmavirus24

_(...) aber das bestehende Verhalten ist gut dokumentiert._

Keine Menge Dokumentation macht aus einer schlechten API eine gute API. Je besser die API, desto weniger Dokumentation benötigt sie.

_Du kannst etwas Ähnliches machen wie (...)_

Richtig, aber das ist sehr irreführend und nicht intuitiv. Ich habe in meinem vorherigen Kommentar beschrieben, was daran falsch ist, files param dafür zu verwenden.

Fazit: Um etwas Besseres zu entwickeln, müssen wir zugeben, dass die aktuelle API in Bezug auf das Senden von multipart/form-data Daten schlecht ist.

@spacecase

_Aus meiner Sicht ist es ziemlich frustrierend, dass Anfragen bereits Unterstützung für mehrteilige Posts haben, dem Benutzer jedoch keinen Zugriff darauf ohne Verwendung einer Datei geben_

Ich stimme zu und deshalb habe ich Ausgabe #935 erstellt.

Dieses Thema ist geschlossen.

Verzeihen Sie mir @kennethreitz, aber @spacecase Ich habe in diesem Beispiel nirgendwo eine Datei verwendet. Ich habe den Parameter files verwendet, um genau das zu tun, was Sie wollen. Hätte ich eine Datei verwendet, hättest du open('filename') .

@sigmavirus24 , das ist leider nicht das was ich will. Dabei spielt es keine Rolle, ob der Client aus einer Datei liest oder nicht. Es ist das, was dem Server mitgeteilt wird. In Ihrem Beispiel lautet der Beitragstext

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

Beachten Sie die Verwendung von filename= . Es teilt dem Server mit, dass eine Datei gesendet wird. In den Web-Apps, mit denen ich arbeite, führt dieses falsche Verhalten zu einem Fehler.

Es tut mir leid, aber dass Sie so anmaßend sind, mir zu sagen, dass es genau das ist, was ich will, beleidigt mich. Ich bin gekommen, um Ideen und Hilfe anzubieten, und es hört sich so an, als ob Sie in diesem Fall beides nicht wollen. Das ist in Ordnung, aber bitte lasse mich nicht kleinreden.

Hm, anscheinend habe ich mich an das Verhalten falsch erinnert. Das tut mir leid. Es war nicht meine Absicht, dich zu beleidigen oder dich herabgeredet zu fühlen. Das ist definitiv genug, um mich dazu zu bringen, einen besseren Weg zu finden, um mit multipart/form-data , aber im Moment bin ich immer noch nicht der Meinung, dass die Ideen für die API überhaupt nicht elegant sind.

@spacecase siehe meine Antwort zu #935. Die Dinge werden besser. Ihr Feedback ist wichtig und wird sehr geschätzt. :)

@spacecase als Geste, um zu zeigen, dass Ihre Meinung wichtig ist, sehen Sie sich sigmavirus24/requests-data-schemes als Notlösung an. Sie müssen Ihren eigenen Content-Type Header festlegen, aber das kann ein kleines Ärgernis sein.

Danke @sigmavirus24 , ich werde es ausprobieren. Es sieht so aus, als würde es den Trick machen.
Ich weiß es zu schätzen, dass du mich nicht beleidigen wolltest. Ich fühle mich damit besser und ich habe nichts gegen Sie oder das Projekt.

Ja, es scheint, dass ich für manche Leute dreist rüberkomme, also muss ich wohl verfeinern, was ich im Internet schreibe. Ich verstehe es nicht und andere haben mir zugestimmt, aber ich arbeite daran. Ich schätze, ich brauche nur eine Stichprobengröße, die groß genug ist, um zu erkennen, was für Leute anstößig ist, ohne dass ich es beabsichtigt habe (abgesehen davon, dass ich mich selbst zum Arsch mache, indem ich mich an etwas erinnere, das so funktioniert, wie es nicht funktioniert).

Ich muss eine Datei und einige Schlüsselwerte posten. Was ist also der richtige Weg, um eine solche Anfrage mit mehreren Teilen zu senden? Ich hoffe ihr könnt mir helfen.

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)

@deertalker für Fragen verwenden Sie bitte StackOverflow . Um Ihre Frage zu beantworten, ist ein Projekt im Gange, um dieses Problem zu beheben sigmavirus24/requests-toolbelt

Beachten Sie auch, dass ich diese Frage bereits auf StackOverflow beantwortet habe, wie Sie hier sehen können .

Ich habe das gleiche Problem beim Generieren von mehrteiligen Beiträgen ohne Datei. Im Moment macht es keinen Unterschied, ob Sie requests.post(url, data=data_dict) oder requests.post(url, data=data_dict, files={}) . Da das Schlüsselwort files standardmäßig auf None , sollten wir in der Lage sein, zwei unterschiedliche Verhaltensweisen zu haben. A multipart/form-data wenn files={} angegeben ist, und application/x-www-form-urlencoded wenn nicht.

Habe ich etwas verpasst?

@jwoillez Hast du dem Stack-Overflow-Link gefolgt, den ich gepostet habe?

Ich denke schon, aber Ihre Antwort dort behandelt Multipart POST mit einer Datei. Ich ging zurück zum ursprünglichen Problem: Multipart POST ohne Datei.

Das Dateiobjekt darf ein String sein. =) Das sollte Ihnen die gewünschten Informationen liefern.

Aber wenn ich deinem Vorschlag folge, komme ich nicht zu so etwas:

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

content
--3eeaadbfda0441b8be821bbed2962e4d--

wobei content die Zeichenfolge ist, zu deren Verwendung Sie mich anstelle der Datei einladen?

Ich bin wirklich nur hinter diesem her:

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

value1
--3eeaadbfda0441b8be821bbed2962e4d

Die Felder im Tupel, die Sie nicht möchten, können als Standardwerte belassen werden:

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

In Zukunft richten Sie Ihre Fragen bitte an StackOverflow. Alle Betreuer behalten es regelmäßig im Auge, und es ist der geeignetere Ort, um diese Fragen zu stellen.

Das ist die Antwort, die ich gesucht habe, danke. Entschuldigung für den Lärm.

Vielleicht noch eine letzte Frage, ist folgendes möglich (angegebener leerer Dateiname, leerer Inhalt)?

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


--3eeaadbfda0441b8be821bbed2962e4d--

Sie können einen leeren Inhalt bereitstellen, indem Sie im Inhaltsabschnitt des Tupels eine leere Zeichenfolge verwenden. Sie können keinen literalen leeren Dateinamen angeben, aber ein nicht vorhandener Dateiname sollte genauso behandelt werden.

Ich habe dies gelegentlich aus verschiedenen seltsamen Gründen tun müssen.
Ich würde diesen Ansatz jedem vorschlagen, der die API in diesem Anwendungsfall stärken möchte:

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)

Oder Sie nutzen den Toolbelt und greifen nicht auf Hacks zurück.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen