色んなAPIを使っているとPOSTで情報取得をするものもあります。
api keyなんかが必要な場合は仕方ないとして、どうでもいいパラメータをPOSTで送るものがREST APIと自称してて??となることも多々。
そんな中で躓くことの多いapplication/x-www-form-urlencoded
のエンコードをどうするのがいいかなと試してみたので覚書き。
基本的にネストしたデータは送らないという前提で書いていきます。
階層化したデータを送る必要があるならapplication/json
で送るかJSON.stringfy
したものを送るようになっているはず。きっとそう。
とはいえ必要な場面が来るかもしれないのでそれっぽいコードも書いておきます。
encodeURIComponent
昔からよく使う方法。
1 2 3 4 5 6 |
const testJson = { key1: 1, key2: "a b" }; const q1 = Object.keys(testJson) .map(key => key + "=" + encodeURIComponent(testJson[key])) .join("&"); console.log(q1); //key1=1&key2=a%20b |
keys()
とencodeURIComponent()
でtestJson
を指定してるのは使いにくいので、実用を考えると関数化するのがよさそう。
1 2 3 4 5 |
const encode = obj => Object.keys(obj) .map(key => key + "=" + encodeURIComponent(obj[key])) .join("&"); console.log(encode(testJson)); |
ものによってはkey
もエンコードが必要なこともあります。
URLSearchParams
スペースの扱い的にこちらの方が推奨されることが多い。
1 2 3 4 |
let url = new URLSearchParams(); url.append("key1", 1); url.append("key2", "a b"); console.log(url.toString()); //key1=1&key2=a+b |
let
使いたくないし手作業も嫌なのでObject.entries()
とArray.reduce()
を使う。
1 2 3 4 5 |
const q2 = Object.entries(testJson).reduce((acc, cur) => { acc.append(...cur); return acc; }, new URLSearchParams()); console.log(q2.toString()); |
URLSearchParams.append()
がURLSearchParams
を返せばさっぱりするのに。
少しきれいにするならこんな感じ。
1 2 3 4 5 |
const q2 = Object.entries(testJson).reduce( (acc, cur) => (acc.append(...cur), acc), new URLSearchParams() ); console.log(q2.toString()); |
ライブラリ
ライブラリが使える環境なら楽だし見た目にもわかりやすいです。
querystring
なら配列を扱えますしqs
なら階層化してようが問題ありません。
ただどちらもスペースが%20
になるエンコード。
1 2 3 4 5 |
const qs = require("qs"); console.log(qs.stringify(testJson)); //key1=1&key2=a%20b const querystring = require("querystring"); console.log(querystring.stringify(testJson)); //key1=1&key2=a%20b |
ネストされたデータ
使うことはないだろうけど一応URLSearchParams
で階層オブジェクトをエンコード。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
const nestAppend = (usp, key, obj) => Object.entries(obj).map(o => { const nkey = `${key}[${o[0]}]`; if (typeof o[1] === "object") nestAppend(usp, nkey, o[1]); else usp.append(nkey, o[1]); }); const encodeQuery = obj => Object.entries(obj).reduce((acc, cur) => { if (typeof cur[1] === "object") cur[1] = nestAppend(acc, ...cur); else acc.append(...cur); return acc; }, new URLSearchParams()); console.log(encodeQuery(testJson).toString()); |
深く考えず勢いで書いたけどqs
でエンコードしたものの%20
が+
に変わったものになったので一応あってそう。
それだけの違いならqs
の結果をreplace
すればいいだけな気もしたけどまぁいいか。