24
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[JS] Fetch APIでFormDataをPOSTするときにContent-Typeを指定しないようにしよう。

Posted at

(久しぶりにSwift以外の記事…)

はじめに

結論はタイトルの通り: 「Fetch APIでFormDataPOSTするときにContent-Typeを指定しないようにしよう。」
その理由は…?

Content-Typeの指定あり/なしで比較

次のコードを実行してみてください。Content-typeをmultipart/form-dataに設定しています。

post.js
const formData = new FormData();
formData.append('name', 'value');
const request = new Request('/do-something', {
  method: 'POST',
  headers: {
    'Content-Type': 'multipart/form-data',
  },
  body: formData,
});
const response = fetch(request);

そうすると次のようなリクエストが送られます(例: Safariの場合; 一部のヘッダは省略)。

Content-Type指定あり
POST /do-something HTTP/1.1
Accept: */*
Content-Type: multipart/form-data
Content-Length: 140
Host: example.com
Accept-Encoding: gzip, deflate, br
Connection: keep-alive

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

value
------WebKitFormBoundaryHwUCy8Y0kR36DASN--

これの何が問題なのでしょう?
既にお気づきの方もいらっしゃるかとは思いますが、次は前出のコードの'Content-Type': 'multipart/form-data',をコメントアウトして実行してみてください。その場合のリクエストは次のようになります。

Content-Type指定なし
POST /do-something HTTP/1.1
Accept: */*
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryRAycGNoAYaQ0xKA0
Content-Length: 140
Host: example.com
Accept-Encoding: gzip, deflate, br
Connection: keep-alive

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

value
------WebKitFormBoundaryRAycGNoAYaQ0xKA0--

さて、Content-Typeを指定する場合と指定しない場合のHTTPリクエストの違いが見えましたか?

Content-Typeを指定しない場合、Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryRAycGNoAYaQ0xKA0のようにクライアントが適切なContent-Typeを設定します。そして大切なのは、FormDataPOSTするときにはContent-Typeのパラメータに"boundary"を含めなければいけないということです1

しかし、JavaScriptでRequestを生成する際にContent-Typeを指定してしまうと、適切なboundaryを指定することができません。すると、サーバ側でmultipartの境界を判別できずにデータの受け取りに失敗することになります。…リクエストボディの1行目をみればboundaryは一目瞭然じゃないかって?もしかしたら「優しいプログラム」は1行目をみてboundaryを判断することがあるかもしれません。しかし、サーバ側のすべてのプログラムがそのように「優しい」ことを期待してはいけません。サーバ側のプログラムを書く人間からすると、リクエストのContent-Typeにboundaryが指定されていない場合に1行目の文字列からそれが勝手にboundaryと判断することはデータを誤って解釈することになりかねず(入れ子になったmultipartとか…実は別のデータだったとか…)、基本的にはboundaryの指定がなければエラーとするでしょう。

もう一度結論

Fetch APIでFormDataPOSTするときにContent-Typeを指定しないようにしよう。

  1. "boundary"は基本的にクライアントがランダムな文字列を含めることが一般的です(実際Safariもリクエストごとに文字列が異なる)

24
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?