application / x-www-form-urlencoded oder multipart / form-data?

1335

In HTTP gibt es zwei Möglichkeiten, Daten zu POSTEN: application/x-www-form-urlencodedund multipart/form-data. Ich verstehe, dass die meisten Browser nur dann Dateien hochladen können, wenn sie multipart/form-dataverwendet werden. Gibt es zusätzliche Anleitungen, wann einer der Codierungstypen in einem API-Kontext verwendet werden soll (kein Browser beteiligt)? Dies könnte zB basieren auf:

  • Datengröße
  • Existenz von Nicht-ASCII-Zeichen
  • Existenz auf (nicht codierten) Binärdaten
  • die Notwendigkeit, zusätzliche Daten zu übertragen (wie Dateiname)

Grundsätzlich habe ich im Web bisher keine formelle Anleitung zur Verwendung der verschiedenen Inhaltstypen gefunden.

max
quelle
74
Es sollte erwähnt werden, dass dies die beiden MIME-Typen sind, die HTML-Formulare verwenden. HTTP selbst hat keine solche Einschränkung ... man kann jeden gewünschten MIME-Typ über HTTP verwenden.
Tybro0103

Antworten:

2013

TL; DR

Zusammenfassung; Wenn Sie binäre (nicht alphanumerische) Daten (oder eine Nutzlast mit beträchtlicher Größe) übertragen möchten, verwenden Sie multipart/form-data. Andernfalls verwenden Sie application/x-www-form-urlencoded.


Die von Ihnen erwähnten MIME-Typen sind die beiden Content-TypeHeader für HTTP-POST-Anforderungen, die Benutzeragenten (Browser) unterstützen müssen. Der Zweck dieser beiden Arten von Anforderungen besteht darin, eine Liste von Name / Wert-Paaren an den Server zu senden. Je nach Art und Menge der übertragenen Daten ist eine der Methoden effizienter als die andere. Um zu verstehen, warum, müssen Sie sich ansehen, was jeder unter der Decke tut.

Für application/x-www-form-urlencoded, aber eine große Abfragezeichenfolge des Körper der HTTP - Nachricht an den Server gesendet wird , im wesentlichen - Namen / Wert - Paare werden voneinander getrennt durch das Zeichen ( &) und Namen von Werten , getrennt durch die gleich sind Symbol ( =). Ein Beispiel hierfür wäre: 

MyVariableOne=ValueOne&MyVariableTwo=ValueTwo

Gemäß der Spezifikation :

[Reservierte und] nicht alphanumerische Zeichen werden durch% HH, ein Prozentzeichen und zwei hexadezimale Ziffern ersetzt, die den ASCII-Code des Zeichens darstellen

Das bedeutet, dass für jedes nicht alphanumerische Byte, das in einem unserer Werte vorhanden ist, drei Bytes erforderlich sind, um es darzustellen. Bei großen Binärdateien ist die Verdreifachung der Nutzlast äußerst ineffizient.

Hier multipart/form-datakommt es ins Spiel. Bei dieser Methode zum Übertragen von Name / Wert-Paaren wird jedes Paar als "Teil" in einer MIME-Nachricht dargestellt (wie in anderen Antworten beschrieben). Teile werden durch eine bestimmte Zeichenfolgengrenze getrennt (speziell ausgewählt, damit diese Grenzzeichenfolge in keiner der Nutzdaten mit "Wert" vorkommt). Jeder Teil hat seine eigenen MIME-Header wie Content-Typeund insbesondere Content-Disposition, die jedem Teil seinen "Namen" geben können. Das Wertstück jedes Namens / Wert-Paares ist die Nutzlast jedes Teils der MIME-Nachricht. Die MIME-Spezifikation bietet uns mehr Optionen bei der Darstellung des Nutzwerts - wir können eine effizientere Codierung von Binärdaten wählen, um Bandbreite zu sparen (z. B. Basis 64 oder sogar Roh-Binärdaten).

Warum nicht die multipart/form-dataganze Zeit nutzen? Bei kurzen alphanumerischen Werten (wie bei den meisten Webformularen) überwiegt der Aufwand für das Hinzufügen aller MIME-Header die Einsparungen durch eine effizientere Binärcodierung erheblich.

Matt Bridges
quelle
84
Hat x-www-form-urlencoded eine Längenbeschränkung oder ist diese unbegrenzt?
Pacerier
34
@Pacerier Das Limit wird vom Server erzwungen, der die POST-Anforderung empfängt. Weitere Informationen finden
Matt Bridges
5
@ZiggyTheHamster JSON und BSON sind jeweils für verschiedene Datentypen effizienter. Base64 ist gzip für beide Serialisierungsmethoden unterlegen. Base64 bringt überhaupt keine Vorteile, HTTP unterstützt binäre Pyloads.
Tiberiu-Ionuț Stan
16
Beachten Sie auch, dass Sie nur Formulardaten auswählen können, wenn ein Formular einen benannten Datei-Upload enthält, da urlencoded keine Möglichkeit hat, den Dateinamen zu platzieren (in Formulardaten ist dies der Name-Parameter für die Inhaltsdisposition).
Guido van Rossum
4
@EML siehe meine Klammer "(speziell ausgewählt, damit diese Grenzzeichenfolge in keiner der" Wert "-Nutzdaten vorkommt)"
Matt Bridges
151

LESEN SIE MINDESTENS DEN ERSTEN PARA HIER!

Ich weiß, dass dies 3 Jahre zu spät ist, aber Matts (akzeptierte) Antwort ist unvollständig und wird Sie schließlich in Schwierigkeiten bringen. Der Schlüssel hier ist, dass, wenn Sie sich für die Verwendung entscheiden multipart/form-data, die Grenze nicht in den Dateidaten erscheinen darf , die der Server schließlich empfängt.

Dies ist kein Problem application/x-www-form-urlencoded, da es keine Grenze gibt. x-www-form-urlencodedkann auch immer mit Binärdaten umgehen, indem einfach ein beliebiges Byte in drei 7BITBytes umgewandelt wird. Ineffizient, aber es funktioniert (und beachten Sie, dass der Kommentar, dass Dateinamen und Binärdaten nicht gesendet werden können, falsch ist; Sie senden ihn einfach als ein weiteres Schlüssel / Wert-Paar).

Das Problem dabei multipart/form-dataist, dass das Grenztrennzeichen nicht in den Dateidaten vorhanden sein darf (siehe RFC 2388 ; Abschnitt 5.2 enthält auch eine ziemlich lahme Entschuldigung dafür, dass kein geeigneter aggregierter MIME-Typ vorhanden ist, der dieses Problem vermeidet).

Also, auf den ersten Blick, multipart/form-dataist von keinem Wert in haupt jede Datei - Upload, binär oder auf andere Weise. Wenn Sie Ihre Grenze nicht korrekt wählen, dann Sie wird schließlich ein Problem haben, ob Sie Text oder Raw - Binary sind zu senden - der Server eine Grenze an der falschen Stelle finden, und die Dateien werden abgeschnitten oder POST wird versagen.

Der Schlüssel besteht darin, eine Codierung und eine Grenze so zu wählen, dass Ihre ausgewählten Grenzzeichen nicht in der codierten Ausgabe erscheinen können. Eine einfache Lösung ist die Verwendung base64(nicht nicht Raw - Binary verwenden). In base64 werden 3 beliebige Bytes in vier 7-Bit-Zeichen codiert, wobei der Ausgabezeichensatz [A-Za-z0-9+/=](dh alphanumerisch, '+', '/' oder '=') ist. =ist ein Sonderfall und wird möglicherweise nur am Ende der codierten Ausgabe als Einzel- =oder Doppelausgabe angezeigt ==. Wählen Sie nun Ihre Grenze als 7-Bit-ASCII-Zeichenfolge, die in der base64Ausgabe nicht angezeigt werden kann. Viele Auswahlmöglichkeiten, die Sie im Internet sehen, schlagen diesen Test fehl - das MDN bildet DokumenteVerwenden Sie beispielsweise "blob" als Grenze beim Senden von Binärdaten - nicht gut. Allerdings so etwas wie "! Blob!" wird niemals in der base64Ausgabe erscheinen .

EML
quelle
52
Während eine Betrachtung von mehrteiligen / Formulardaten die Sicherstellung ist, dass die Grenze nicht in den Daten erscheint, ist dies ziemlich einfach zu erreichen, indem eine Grenze gewählt wird, die ausreichend lang ist. Bitte verwenden Sie nicht die Base64-Codierung, um dies zu erreichen. Eine zufällig generierte Grenze mit der gleichen Länge wie eine UUID sollte ausreichen: stackoverflow.com/questions/1705008/… .
Joshcodes
20
@EML, das macht überhaupt keinen Sinn. Offensichtlich wird die Grenze automatisch vom http-Client (Browser) ausgewählt, und der Client ist intelligent genug, um keine Grenze zu verwenden, die mit dem Inhalt Ihrer hochgeladenen Dateien kollidiert. Es ist so einfach wie ein Teilstring-Match index === -1.
Pacerier
13
@ Pacerier: (A) Lesen Sie die Frage: "Kein Browser beteiligt, API-Kontext". (B) Browser erstellen sowieso keine Anfragen für Sie. Sie machen es selbst, manuell. In Browsern steckt keine Magie.
EML
12
@ BeniBela, er wird wahrscheinlich vorschlagen, '()+-./:=dann zu verwenden . Die zufällige Generierung mit Teilstring-Prüfung ist jedoch noch nicht abgeschlossen und kann mit einer Zeile erfolgen : while(true){r = rand(); if(data.indexOf(r) === -1){doStuff();break;}}. Der Vorschlag von EML (Konvertierung in base64, nur um übereinstimmende Teilzeichenfolgen zu vermeiden) ist einfach seltsam, ganz zu schweigen von unnötigen Leistungseinbußen. Und all die Probleme umsonst, da der Einzeilenalgorithmus ebenso einfach und unkompliziert ist. Base64 soll nicht (ab) auf diese Weise verwendet werden, da der HTTP-Body alle 8-Bit- Oktette akzeptiert .
Pacerier
31
Diese Antwort fügt nicht nur nichts zur Diskussion hinzu, sondern gibt auch falsche Ratschläge. Erstens ist es bei der Übertragung von Zufallsdaten in getrennten Teilen immer möglich, dass die gewählte Grenze in der Nutzlast vorhanden ist. Der EINZIGE Weg, um sicherzustellen, dass dies nicht geschieht, besteht darin, die gesamte Nutzlast für jede Grenze zu untersuchen, die wir uns ausgedacht haben. Völlig unpraktisch. Wir akzeptieren nur die infinitesimale Wahrscheinlichkeit einer Kollision und finden eine vernünftige Grenze, wie "--- Grenze- <UUID hier> -Grenze ---". Zweitens verschwendet die Verwendung von Base64 immer Bandbreite und füllt die Puffer ohne Grund.
Vagelis
92

Ich glaube nicht, dass HTTP auf POST in mehrteiligen oder x-www-form-urlencoded beschränkt ist. Der Content-Type-Header ist orthogonal zur HTTP-POST-Methode (Sie können den für Sie geeigneten MIME-Typ eingeben). Dies ist auch bei typischen Webapps auf der Basis von HTML-Darstellungen der Fall (z. B. wurde die json-Nutzlast für die Übertragung von Nutzdaten für Ajax-Anforderungen sehr beliebt).

In Bezug auf Restful API over HTTP sind application / xml und application / json die beliebtesten Inhaltstypen, mit denen ich in Kontakt gekommen bin.

application / xml:

  • Datengröße: XML ist sehr ausführlich, aber normalerweise kein Problem, wenn die Komprimierung verwendet wird und der Schreibzugriffsfall (z. B. über POST oder PUT) viel seltener ist als der Lesezugriff (in vielen Fällen macht er <3% des gesamten Datenverkehrs aus ). Selten gab es Fälle, in denen ich die Schreibleistung optimieren musste
  • Vorhandensein von Nicht-ASCII-Zeichen: Sie können utf-8 als Codierung in XML verwenden
  • Existenz von Binärdaten: müsste Base64-Codierung verwenden
  • Dateinamen: Sie können dieses interne Feld in XML kapseln

Anwendung / json

  • Datengröße: kompakter weniger als XML, immer noch Text, aber Sie können komprimieren
  • Nicht-ASCII-Zeichen: json ist utf-8
  • Binärdaten: base64 (siehe auch json-binary-question )
  • Dateinamendaten: Kapselung als eigener Feldabschnitt in json

Binärdaten als eigene Ressource

Ich würde versuchen, Binärdaten als eigenes Asset / Ressource darzustellen. Es fügt einen weiteren Anruf hinzu, entkoppelt aber das Zeug besser. Beispielbilder:

POST /images
Content-type: multipart/mixed; boundary="xxxx" 
... multipart data

201 Created
Location: http://imageserver.org/../foo.jpg  

In späteren Ressourcen können Sie die binäre Ressource einfach als Link einbinden:

<main-resource>
 ...
 <link href="http://imageserver.org/../foo.jpg"/>
</main-resource>
Manuel Aldana
quelle
Interessant. Aber wann soll application / x-www-form-urlencoded verwendet werden und wann multipart / form-data?
Max
3
application / x-www-form-urlencoded ist der Standard-MIME-Typ Ihrer Anfrage (siehe auch w3.org/TR/html401/interact/forms.html#h-17.13.4 ). Ich benutze es für "normale" Webformulare. Für die API verwende ich application / xml | json. Multipart / Formulardaten sind eine Glocke beim Denken an Anhänge (innerhalb des Antwortkörpers sind mehrere Datenabschnitte mit einer definierten Grenzzeichenfolge verkettet).
Manuel Aldana
4
Ich denke, das OP hat wahrscheinlich nur nach den beiden Typen gefragt, die HTML-Formulare verwenden, aber ich bin froh, dass darauf hingewiesen wurde.
tybro0103
30

Ich stimme vielem zu, was Manuel gesagt hat. In der Tat beziehen sich seine Kommentare auf diese URL ...

http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4

... welche Staaten:

Der Inhaltstyp "application / x-www-form-urlencoded" ist ineffizient, um große Mengen von Binärdaten oder Text mit Nicht-ASCII-Zeichen zu senden. Der Inhaltstyp "Multipart / Formulardaten" sollte zum Senden von Formularen verwendet werden, die Dateien, Nicht-ASCII-Daten und Binärdaten enthalten.

Für mich würde es jedoch auf die Unterstützung von Tools / Frameworks ankommen.

  • Mit welchen Tools und Frameworks erwarten Sie, dass Ihre API-Benutzer ihre Apps erstellen?
  • Haben sie Frameworks oder Komponenten, die sie verwenden können und die eine Methode der anderen vorziehen?

Wenn Sie eine klare Vorstellung von Ihren Benutzern haben und wissen, wie sie Ihre API verwenden, hilft Ihnen dies bei der Entscheidung. Wenn Sie Ihren API-Benutzern das Hochladen von Dateien erschweren, werden sie entfernt, und Sie werden viel Zeit damit verbringen, sie zu unterstützen.

Zweitens wäre die Toolunterstützung, die SIE zum Schreiben Ihrer API haben, und wie einfach es für Sie ist, einen Upload-Mechanismus über den anderen zu setzen.

Martin Peck
quelle
1
Hallo, bedeutet das, dass wir jedes Mal, wenn wir etwas auf dem Webserver veröffentlichen, den Inhaltstyp angeben müssen, um den Webserver wissen zu lassen, ob er die Daten entschlüsseln soll? Auch wenn wir die http-Anfrage selbst erstellen, MÜSSEN wir den Inhaltstyp erwähnen, oder?
GMsoF
2
@ GMsoF, es ist optional. Siehe stackoverflow.com/a/16693884/632951 . Möglicherweise möchten Sie die Verwendung des Inhaltstyps vermeiden, wenn Sie eine bestimmte Anforderung für einen bestimmten Server erstellen, um generischen Overhead zu vermeiden.
Pacerier
2

Nur ein kleiner Hinweis von meiner Seite zum Hochladen von HTML5-Canvas-Bilddaten:

Ich arbeite an einem Projekt für eine Druckerei und hatte einige Probleme beim Hochladen von Bildern auf den Server, die von einem HTML5- canvasElement stammen. Ich hatte mindestens eine Stunde lang Probleme und konnte das Bild nicht richtig auf meinem Server speichern.

Nachdem ich die contentTypeOption meines jQuery-Ajax-Aufrufs auf application/x-www-form-urlencodedalles gesetzt hatte, lief alles richtig und die base64-codierten Daten wurden korrekt interpretiert und erfolgreich als Bild gespeichert.


Vielleicht hilft das jemandem!

Torsten Barthel
quelle
4
Welchen Inhaltstyp hat es gesendet, bevor Sie es geändert haben? Dieses Problem könnte darauf zurückzuführen sein, dass der Server den Inhaltstyp, als den Sie ihn gesendet haben, nicht unterstützt.
Catorda
1

Wenn Sie Content-Type = x-www-urlencoded-form verwenden müssen, verwenden Sie NICHT FormDataCollection als Parameter: In asp.net Core 2+ hat FormDataCollection keine Standardkonstruktoren, die von Formatierern benötigt werden. Verwenden Sie stattdessen IFormCollection:

 public IActionResult Search([FromForm]IFormCollection type)
    {
        return Ok();
    }
Jahansha
quelle