JavaScript

fetchに渡すFormDataに key[]=value 方式で配列を詰める

配列をPOSTするとき、jQueryだと以下のように書けます。

$.post('http://example.com', {

ids: [1,2,3]
})

このとき、以下のようなリクエストが送信されます。

image.png

注目してほしいのはContent-TypeとForm Dataです。Content-Typeは application/x-www-form-urlencoded なので、Form送信時に使用されるContent-Typeで、FormDataが送られています。 key[]=value 形式の配列はサーバサイドでは [1,2,3] のような配列にパースされます。

同じ物をFetch APIを使って、書いてみます。fetchでFormのように送信したい場合、bodyにFormDataオブジェクトをセットすればOKです。したがって、以下のように書けば動きそう…ですが、このコードは↑と同じようには動きません。

const data = new FormData();

data.append('ids', [1,2,3]);

fetch('http://example.com', {
method: 'POST',
body: data,
mode: 'no-cors',
})

このとき送信されるリクエストは以下のような形になります。

image.png

Content-Typeはマルチパートになっていますが、フォームとして送信できているので問題ありません。問題はForm Dataで、キーは ids 、値には [1,2,3]toString() したものが入っています。これはこれで、サーバサイドのプログラムでカンマ区切りをsplitしてもよいのですが、既存APIが key[]=value 方式を受け付けるようになっていると、このままでは上手くいきません。

多少トリッキーですが、値が配列である場合にkeyをいじる処理を追加することで、fetch + FormDataで key[]=value 方式のデータを送信できます。

function createFormData(data) {

const form = new FormData();

Object.keys(data).forEach(key => {
const value = data[key];
if (Array.isArray(value)) {
value.forEach(entry => {
form.append(key + '[]', entry);
});
} else {
form.append(key, value);
}
});

return form;
}

fetch('http://example.com', {
method: 'POST',
mode: 'no-cors',
body: createFormData({ ids: [1,2,3] }),
});

image.png