In HTTP gibt es zwei Möglichkeiten, POST Daten zu senden: application/x-www-form-urlencoded
und multipart/form-data
. Ich verstehe, dass die meisten Browser nur Dateien hochladen können, wenn multipart/form-data
verwendet wird. Gibt es zusätzliche Hinweise zur Verwendung eines der Codierungstypen in einem API-Kontext (kein Browser beteiligt)? Dies könnte z.B. basiert auf:
Grundsätzlich habe ich im Web keine formelle Anleitung zur Verwendung der verschiedenen Inhaltstypen gefunden.
TL; DR
Zusammenfassung; Wenn Sie binäre (nicht alphanumerische) Daten (oder eine Nutzlast von beträchtlicher Größe) übertragen möchten, verwenden Sie multipart/form-data
. Verwenden Sie andernfalls application/x-www-form-urlencoded
.
Die MIME-Typen, die Sie erwähnen, sind die beiden Content-Type
-Header 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 schauen, was jeder unter der Decke tut.
Bei application/x-www-form-urlencoded
besteht der Hauptteil der an den Server gesendeten HTTP-Nachricht im Wesentlichen aus einer riesigen Abfragezeichenfolge - Name/Wert-Paare sind durch das kaufmännische Und (&
) und Namen von Werten durch das getrennt Gleichheitszeichen (=
). Ein Beispiel hierfür wäre:
MyVariableOne=ValueOne&MyVariableTwo=ValueTwo
Nach der Spezifikation :
[Reservierte und] nicht-alphanumerische Zeichen werden durch% HH, ein Prozentzeichen und zwei Hexadezimalziffern 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 höchst ineffizient.
Hier kommt multipart/form-data
ins Spiel. Bei dieser Methode zum Übertragen von Name/Wert-Paaren wird jedes Paar als "Teil" in einer MIME-Nachricht dargestellt (wie durch andere Antworten beschrieben). Teile werden durch eine bestimmte Zeichenkettengrenze getrennt (speziell ausgewählt, damit diese Zeichenkette in keiner der "Wert" -Nutzlasten vorkommt). Jeder Teil hat seine eigenen MIME-Header wie Content-Type
und insbesondere Content-Disposition
, die jedem Teil seinen "Namen" geben können. Das Wertstück jedes Name/Wert-Paares ist die Nutzlast jedes Teils der MIME-Nachricht. Die MIME-Spezifikation bietet uns mehr Optionen bei der Darstellung der Nutzlast. Wir können eine effizientere Codierung von Binärdaten wählen, um Bandbreite zu sparen (z. B. Base 64 oder sogar Raw Binary).
Warum nicht die ganze Zeit multipart/form-data
verwenden? Bei kurzen alphanumerischen Werten (wie bei den meisten Webformularen) wird der Aufwand für das Hinzufügen aller MIME-Header die Einsparungen durch eine effizientere Binärcodierung erheblich aufwiegen.
LESEN SIE AT HIER MINDEST DU DEN ERSTEN ABSATZ!
Ich weiß, dass dies 3 Jahre zu spät ist, aber Matts (akzeptierte) Antwort ist unvollständig und wird Sie irgendwann in Schwierigkeiten bringen. Der Schlüssel hier ist, dass, wenn Sie multipart/form-data
verwenden, die Grenze nicht in den Dateidaten erscheinen darf, die der Server schließlich empfängt.
Dies ist kein Problem für application/x-www-form-urlencoded
, da es keine Grenze gibt. x-www-form-urlencoded
kann auch immer Binärdaten verarbeiten, indem einfach ein beliebiges Byte in drei 7BIT
Bytes umgewandelt wird. Ineffizient, aber es funktioniert (und beachten Sie, dass der Kommentar, dass keine Dateinamen und keine Binärdaten gesendet werden können, falsch ist; Sie senden ihn einfach als ein anderes Schlüssel/Wert-Paar).
Das Problem mit multipart/form-data
ist, dass das Begrenzungstrennzeichen nicht in den Dateidaten vorhanden sein darf (siehe RFC 2388 ; Abschnitt 5.2 enthält auch eine eher lahme Entschuldigung für das Fehlen eines geeigneten aggregierten MIME-Typs vermeidet dieses Problem).
Auf den ersten Blick hat multipart/form-data
in jedem Datei-Upload, binär oder auf andere Weise, keinerlei Wert. Wenn Sie Ihre Grenze nicht richtig wählen, haben Sie irgendwann ein Problem, egal ob Sie Nur-Text oder Binär-Rohdaten senden - der Server findet eine Grenze im falschen Format platzieren, und Ihre Datei wird abgeschnitten, oder die POST wird fehlschlagen.
Der Schlüssel besteht darin, eine Kodierung und eine Begrenzung so zu wählen, dass die von Ihnen ausgewählten Begrenzungszeichen nicht in der kodierten Ausgabe erscheinen können. Eine einfache Lösung besteht darin, base64
zu verwenden (do not use raw binary). In base64 3 werden beliebige Bytes in vier 7-Bit-Zeichen codiert, wobei der Ausgabezeichensatz [A-Za-z0-9+/=]
ist (d. H. Alphanumerische Zeichen, '+', '/' oder '='). =
ist ein Sonderfall und wird möglicherweise nur am Ende der codierten Ausgabe als einzelner =
oder doppelter ==
angezeigt. Wählen Sie nun Ihre Grenze als 7-Bit-Zeichenfolge ASCII aus, die in der Ausgabe von base64
nicht angezeigt werden kann. Viele Optionen, die Sie im Internet sehen, schlagen bei diesem Test fehl - die MDN-Formulare docs verwenden beispielsweise "blob" als Grenze beim Senden von Binärdaten - nicht gut. Etwas wie "! Blob!" wird niemals in der Ausgabe von base64
erscheinen.
Ich glaube nicht, dass HTTP in mehrteiligen oder x-www-form-urlencoded auf POST beschränkt ist. Der Content-Type Header ist orthogonal zur HTTP POST -Methode (Sie können den für Sie passenden MIME-Typ eingeben). Dies ist auch der Fall für typische HTML-darstellungsbasierte Webanwendungen (z. B. JSON-Payload wurde zum Übertragen von Payload für Ajax-Anforderungen sehr beliebt).
In Bezug auf Restful API über HTTP waren die beliebtesten Inhaltstypen, mit denen ich in Kontakt kam, application/xml und application/json.
Ich würde versuchen, binäre Daten als eigenes Asset/Ressource darzustellen. Es fügt einen weiteren Aufruf 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ärressource einfach als Link einfügen:
<main-resource>
...
<link href="http://imageserver.org/../foo.jpg"/>
</main-resource>
Ich stimme vielem zu, was Manuel gesagt hat. In der Tat verweisen 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 käme es jedoch auf die Unterstützung von Tools/Frameworks an.
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 wegziehen, und Sie werden viel Zeit für deren Unterstützung aufwenden.
Zweitens wäre dies die Unterstützung des Tools, das SIE zum Schreiben Ihrer API haben, und wie einfach es für Sie ist, einen Upload-Mechanismus über den anderen zu bringen.
Ich arbeite an einem Projekt für eine Druckerei und hatte einige Probleme beim Hochladen von Bildern auf den Server, die von einem HTML5 canvas
-Element stammten. Ich hatte mindestens eine Stunde lang Probleme und konnte das Bild nicht richtig auf meinem Server speichern.
Nachdem ich die Option contentType
meines jQuery-Ajax-Aufrufs auf application/x-www-form-urlencoded
gesetzt hatte, lief alles richtig und die Base64-codierten Daten wurden korrekt interpretiert und erfolgreich als Bild gespeichert.
Vielleicht hilft das jemandem!
Wenn Sie Content-Type = x-www-urlencoded-form verwenden müssen, verwenden Sie FormDataCollection NICHT als Parameter: In asp.net Core 2+ verfügt FormDataCollection über keine Standardkonstruktoren, die von Formatters benötigt werden. Verwenden Sie stattdessen IFormCollection:
public IActionResult Search([FromForm]IFormCollection type)
{
return Ok();
}