レッスン 3
encodeURI と encodeURIComponent
JavaScript ヘルパーと、パス vs クエリ値への適用タイミング。
JavaScript は名前がほぼ同じ 2 つのグローバル関数を公開しますが、エスケープしない文字集合が異なります。どちらも URI テキスト向けですが、想定するデータスコープが違います。
encodeURIComponent は「単一コンポーネント」をエンコード
クエリ値、パスセグメント、フラグメント識別子、構造を注入してはいけない任意の文字列に使います。
encodeURIComponent("a/b?c=1&d=2");
// "a%2Fb%3Fc%3D1%26d%3D2"
/, ?, &, = がすべて percent エスケープになり、埋め込み時に値が不透明のままです。
手動でクエリ文字列を組む典型パターン:
const q = new URLSearchParams({
title: "Q&A / Notes",
next: "https://example.com/a?x=1",
});
q.toString(); // title=Q%26A+%2F+Notes&next=https%3A%2F%2Fexample.com%2Fa%3Fx%3D1
// (URLSearchParams は application/x-www-form-urlencoded ルール; スペースは +)
手配線文字列では、区切り文字を既に処理する URLSearchParams またはライブラリを優先。
encodeURI は URI 区切り文字を保持
encodeURI はほぼ完成した URIを渡し、グローバルに不正な文字だけ直し、構造的句読点は意味のあるまま残すと仮定します。
encodeURI("https://example.com/résumé?id=1#top");
// https://example.com/r%C3%A9sum%C3%A9?id=1#top
?, #, / などは本当の区切りかもしれないためエンコードしません — 「データ」にそれらが含まれると危険。
encodeURI("https://example.com/search?q=a/b");
// スラッシュを残す — / がデータならサーバー期待を壊す可能性
経験則: 任意ユーザー文字列を保護するのに encodeURI を使わない; コンポーネントごとに encodeURIComponent(または上位 API)。
クイック判断ガイド
| 状況 | 推奨ヘルパー |
|---|---|
/ をデータとして含む単一パスセグメント | 各セグメントを encodeURIComponent |
| 区切り不明のユーザーからの完全 URL 文字列 | URL でパースし、各部分をエンコード |
| 構造は既に健全、発音記号だけ失敗 | 場合によって encodeURI — 多くは host/path を別途正規化の方が明確 |
| API を繰り返し構築 | 連結より URL + URLSearchParams |
decodeURI vs decodeURIComponent
対称的な注意:
decodeURIComponent("100%2525"); // 1 回デコードで "100%25" — 上流の二重エンコードバグに注意
decodeURI("http://host/%7Euser") → 他位置のエスケープをデコードしつつ区切りは触らない
エンコード/デコードスコープを不一致にすると、デコードテキストが区切り文字(&, ?)を再導入し、パラメータ解析が変わる — HTML やヘッダーに反射するときの古典的 open-redirect や filter-bypass 落とし穴。