application / x-www-form-urlencodedまたはmultipart / form-data?


1335

HTTPでありPOSTデータには2つの方法がありますapplication/x-www-form-urlencodedmultipart/form-data。ほとんどのブラウザは、multipart/form-dataが使用されている場合にのみファイルをアップロードできることを理解しています。APIコンテキストでいずれかのエンコードタイプを使用する場合の追加のガイダンスはありますか(ブラウザーは関与しません)?これは、たとえば以下に基づいている可能性があります。

  • データサイズ
  • 非ASCII文字の存在
  • (エンコードされていない)バイナリデータの存在
  • 追加のデータ(ファイル名など)を転送する必要性

基本的に、これまでのところ、さまざまなコンテンツタイプの使用に関する正式なガイダンスはWeb上にありませんでした。


74
これらは、HTMLフォームが使用する2つのMIMEタイプであることに注意してください。HTTP自体にはそのような制限はありません... HTTPを介して彼が望むどんなMIMEタイプでも使用できます。
tybro0103 2014年

回答:


2013

TL; DR

概要; 送信するバイナリ(非英数字)データ(またはかなり大きなサイズのペイロード)がある場合は、を使用しますmultipart/form-data。それ以外の場合は、を使用しますapplication/x-www-form-urlencoded


言及するMIMEタイプは、Content-Typeユーザーエージェント(ブラウザ)がサポートする必要があるHTTP POSTリクエストの2つのヘッダーです。これら両方のタイプのリクエストの目的は、名前と値のペアのリストをサーバーに送信することです。送信されるデータの種類と量に応じて、いずれかの方法が他の方法よりも効率的になります。理由を理解するには、それぞれが何をしているのかを内部で確認する必要があります。

の場合application/x-www-form-urlencoded、サーバーに送信されるHTTPメッセージの本文は基本的に1つの巨大なクエリ文字列です。名前と値のペアはアンパサンド(&)で区切られ、名前は等号(=)で値から区切られます。この例は次のとおりです。 

MyVariableOne=ValueOne&MyVariableTwo=ValueTwo

仕様によると:

[予約済みおよび]英数字以外の文字は、「%HH」、パーセント記号、および文字のASCIIコードを表す2つの16進数で置き換えられます

つまり、値の1つに存在する英数字以外のバイトごとに、それを表すために3バイトが必要になります。大きなバイナリファイルの場合、ペイロードを3倍にすることは非常に非効率的です。

そこで登場multipart/form-dataするのが、名前と値のペアを送信するこの方法では、各ペアはMIMEメッセージで「パート」として表されます(他の回答で説明されています)。パーツは特定の文字列境界で区切られています(この境界文字列が「値」のペイロードのいずれにも出現しないように特別に選択されています)。各部分には、Content-Type特にのような独自のMIMEヘッダーのセットContent-Dispositionがあり、各部分に「名前」を付けることができます。各名前と値のペアの値の部分は、MIMEメッセージの各部分のペイロードです。値のペイロードを表す場合、MIME仕様により多くのオプションが提供されます。帯域幅を節約するために、バイナリデータのより効率的なエンコーディングを選択できます(たとえば、base 64またはrawバイナリ)。

multipart/form-dataいつも使ってみませんか?(ほとんどのWebフォームのように)短い英数字の値の場合、すべてのMIMEヘッダーを追加するオーバーヘッドは、より効率的なバイナリエンコーディングによる節約を大幅に上回ります。


84
x-www-form-urlencodedには長さの制限がありますか、それとも無制限ですか?
Pacerier 2013年

34
@Pacerierこの制限は、POSTリクエストを受信するサーバーによって適用されます。詳細については、次のスレッドを参照してください。stackoverflow.com
Matt Bridges

5
@ZiggyTheHamster JSONとBSONは、それぞれ異なるタイプのデータに対してより効率的です。どちらのシリアル化方法でも、Base64はgzipよりも劣ります。Base64には何の利点もありません。HTTPはバイナリpyloadをサポートしています。
Tiberiu-Ionuțスタン

16
また、フォームに名前付きファイルのアップロードが含まれている場合、urlencodedにはファイル名を配置する方法がないため、フォームデータしか選択できないことに注意してください(form-dataには、content-dispositionの名前パラメーターです)。
グイドファンロッサム2014年

4
@EMLは私の括弧を参照します(「この境界文字列が「値」のペイロードで発生しないように特別に選択されています)」
Matt Bridges

151

ここで最初のパラを少なくとも読んでください!

私はこれが3年では遅すぎることを知っていますが、Mattの(受け入れられた)回答は不完全であり、最終的にはトラブルに巻き込まれます。ここで重要なのは、を使用することを選択した場合multipart/form-data、サーバーが最終的に受信するファイルデータに境界が表示されないようにすることです。

application/x-www-form-urlencoded境界がないため、これはの問題ではありません。x-www-form-urlencodedまた、任意の1バイトを3 7BITバイトに変換するという簡単な方法で、常にバイナリデータを処理できます。非効率的ですが、機能します(ファイル名とバイナリデータを送信できないというコメントは正しくありません。別のキーと値のペアとして送信するだけです)。

の問題multipart/form-dataは、境界区切り記号がファイルデータ内に存在してはならないことです(RFC 2388を参照してください。セクション5.2には、この問題を回避する適切な集約MIMEタイプがないためのかなり言い訳も含まれています)。

したがって、一見したところ、バイナリまたはその他のファイルのアップロードでmultipart/form-dataの価値もありません。あなたが正しくあなたの境界線を選択しない場合は、ます、サーバーが間違った場所に境界を見つけるだろうし、あなたのファイルが切り捨てられます、またはPOST -最終的にあなたは、プレーンテキストや生のバイナリを送っているかどうか、問題を抱えています失敗します。

重要なのは、選択した境界文字がエンコードされた出力に表示されないように、エンコーディングと境界を選択することです。1つの簡単な解決策は、使用することですbase64(生のバイナリを使用しないでください)。BASE64 3つの任意のバイトが出力文字セットが4個の7ビット文字にエンコードされる[A-Za-z0-9+/=](すなわち英数字、「+」、「/」または「=」)。=特殊なケースであり、単一のように、符号化された出力の最後に表示されることがあり=、またはダブル==。次に、base64出力に表示できない7ビットASCII文字列として境界を選択します。あなたがネットで見る多くの選択はこのテストに失敗します-MDNフォームドキュメントたとえば、バイナリデータを送信するときの境界として「blob」を使用します-良くありません。ただし、「!blob!」のようなもの base64出力には表示されません。


52
multipart / form-dataの考慮事項は、境界がデータに表示されないことを保証することですが、これは、十分に長い境界を選択することで達成するのはかなり簡単です。これを実現するためにbase64エンコードを使用しないでください。ランダムに生成され、UUIDと同じ長さの境界で十分です:stackoverflow.com/questions/1705008/…
Joshcodes、2014

20
@EML、これはまったく意味がありません。明らかに、境界はhttpクライアント(ブラウザ)によって自動的に選択され、クライアントはアップロードされたファイルのコンテンツと衝突する境界を使用しないように十分スマートになります。単純なaa substring match index === -1です。
Pacerier 2014

13
@Pacerier:(A)質問を読みます:「ブラウザーは含まれていません、APIコンテキスト」。(B)ブラウザはとにかくあなたのためのリクエストを構築しません。自分で手動で行います。ブラウザには魔法はありません。
EML 2014

12
@BeniBela、彼はおそらく'()+-./:=それから使うことを提案するでしょう。それでも、部分文字列チェックを使用したランダム生成は依然として有効であり、1行で実行できますwhile(true){r = rand(); if(data.indexOf(r) === -1){doStuff();break;}}。EMLの提案(部分文字列の一致を回避するためだけにbase64に変換する)は、不必要なパフォーマンスの低下が伴うことは言うまでもなく、奇妙なことです。また、1行のアルゴリズムも同じように単純で単純なので、すべての問題は無料です。HTTP本体はすべての8ビットオクテットを受け入れるので、Base64はこのように(ab)使用されることを意図していません。
Pacerier、2015

31
この答えは、議論に何も追加しないだけでなく、間違ったアドバイスも与えます。第1に、ランダムなデータを別々の部分で送信する場合は常に、選択した境界がペイロードに存在する可能性があります。これが発生しないことを確認する唯一の方法は、考え出された各境界のペイロード全体を調べることです。まったく実用的ではありません。衝突の微小確率を受け入れて、「--- boundary- <UUID here> -boundary ---」のような妥当な境界を考え出します。次に、常にBase64を使用すると、帯域幅を浪費し、理由もなくバッファがいっぱいになります。
vagelis 2016

92

HTTPがマルチパートのPOSTまたはx-www-form-urlencodedに制限されているとは思いません。Content-Typeのヘッダーは、 HTTP POSTメソッドに直交する(あなたはどのスーツあなたのMIMEタイプを埋めることができます)。これは、典型的なHTML表現ベースのWebアプリケーションの場合にも当てはまります(たとえば、jsonペイロードは、ajaxリクエストのペイロードを送信するために非常に普及しました)。

Restful API over HTTPに関して、私が連絡を取った最も人気のあるコンテンツタイプはapplication / xmlとapplication / jsonです。

application / xml:

  • data-size:XMLは非常に冗長ですが、通常、圧縮を使用し、書き込みアクセスのケース(たとえば、POSTまたはPUTによる)が読み取りアクセスよりもはるかにまれであると考える場合は問題になりません(多くの場合、すべてのトラフィックの3%未満です) )。まれに、書き込みパフォーマンスを最適化する必要があった場合
  • 非ASCII文字の存在:XMLのエンコーディングとしてutf-8を使用できます
  • バイナリデータの存在:base64エンコーディングを使用する必要があります
  • ファイル名データ:この内部フィールドをXMLでカプセル化できます

application / json

  • data-size:XMLよりもコンパクトで、テキストのままですが、圧縮できます
  • 非ASCII文字:jsonはutf-8です
  • バイナリデータ:base64(json-binary-questionも参照)
  • ファイル名データ:json内の独自のフィールドセクションとしてカプセル化

独自のリソースとしてのバイナリデータ

バイナリデータを独自のアセット/リソースとして表現しようと思います。それは別の呼び出しを追加しますが、ものをよりよく分離します。画像の例:

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

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

後のリソースでは、リンクとしてバイナリリソースを単純にインライン化できます。

<main-resource>
 ...
 <link href="http://imageserver.org/../foo.jpg"/>
</main-resource>

面白い。しかし、application / x-www-form-urlencodedをいつ使用し、multipart / form-dataを使用するのですか?
最大

3
application / x-www-form-urlencodedは、リクエストのデフォルトのMIMEタイプです(w3.org/TR/html401/interact/forms.html#h-17.13.4参照)。「通常の」ウェブフォームに使用します。APIの場合、application / xml | jsonを使用します。multipart / form-dataは、アタッチメントを考える際の鐘です(応答本文内では、いくつかのデータセクションが定義された境界文字列と連結されています)。
manuel aldana

4
OPはおそらくHTMLフォームが使用する2つのタイプについて単に尋ねていたと思いますが、これが指摘されてうれしいです。
tybro0103 2014年

30

マヌエルが言ったことの多くに同意します。実際、彼のコメントはこのURLを参照しています...

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

... どの州:

コンテンツタイプ「application / x-www-form-urlencoded」は、非ASCII文字を含む大量のバイナリデータまたはテキストを送信する場合には非効率的です。コンテンツタイプ「multipart / form-data」は、ファイル、非ASCIIデータ、およびバイナリデータを含むフォームを送信するために使用する必要があります。

しかし、私にとっては、ツール/フレームワークのサポートに帰着します。

  • APIユーザーがアプリを構築するためにどのツールとフレームワークを期待していますか?
  • 1つの方法を他の方法よりも優先して使用できるフレームワークまたはコンポーネントはありますか?

ユーザーの明確なアイデアと、ユーザーがAPIをどのように使用するかが明確になれば、それが判断に役立ちます。APIユーザーにとってファイルのアップロードを困難にすると、それらのユーザーは離れてしまい、サポートに多くの時間を費やすことになります。

これに次ぐのは、APIを作成するためのツールサポートと、1つのアップロードメカニズムを他のメカニズムに簡単に対応させることがどれほど簡単になるかです。


1
こんにちは、Webサーバーに何かを投稿するたびに、データをデコードする必要があることをWebサーバーに知らせるために、コンテンツタイプとは何かを言及する必要があるという意味ですか?自分でhttpリクエストを作成したとしても、コンテンツタイプについて言及する必要がありますか?
GMsoF 2013

2
@GMsoF、それはオプションです。stackoverflow.com/a/16693884/632951を参照してください。一般的なオーバーヘッドを回避するために、特定のサーバーに対する特定のリクエストを作成する場合は、content-typeの使用を避けたい場合があります。
Pacerier 2014

2

HTML5キャンバス画像データをアップロードするための私の側からのほんの少しのヒント:

プリントショップのプロジェクトに取り組んでいますが、HTML5 canvas要素から取得した画像をサーバーにアップロードしたために問題が発生しました。少なくとも1時間は苦労していましたが、サーバーに画像を正しく保存することができませんでした。

contentTypejQuery ajax呼び出しのオプションをapplication/x-www-form-urlencodedすべてに設定する と、すべてが正しく行われ、base64でエンコードされたデータが正しく解釈され、画像として正常に保存されました。


多分それは誰かを助ける!


4
変更する前に送信していたコンテンツタイプは何ですか?この問題は、送信元のコンテンツタイプがサーバーでサポートされていないことが原因である可能性があります。
catorda

1

Content-Type = x-www-urlencoded-formを使用する必要がある場合は、パラメーターとしてFormDataCollectionを使用しないでください。asp.netCore 2+では、FormDataCollectionには、フォーマッターで必要とされるデフォルトのコンストラクターがありません。代わりにIFormCollectionを使用してください:

 public IActionResult Search([FromForm]IFormCollection type)
    {
        return Ok();
    }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.