JavaScript Tips | 文字列ユーティリティ:URL 系 - URL デコード

JavaScript JavaScript
スポンサーリンク

「URL デコード」で何を取り戻したいのか

URL デコードは、URL の中で「%E3%81%82」みたいに変換されてしまった文字を、人間が読める元の文字列に戻すことです。

エンコードは「安全に送るための変換」。
デコードは「受け取ったあとに元に戻す変換」。

検索画面の ?q=%E6%A4%9C%E7%B4%A2 を「検索」という文字列に戻したり、
A%26BA&B に戻したりするのが、URL デコードの役目です。


JavaScript の 2 つの基本関数:decodeURI と decodeURIComponent

まずは名前と役割を対応させる

エンコードと同じく、デコードにも 2 種類あります。

decodeURI("https://example.com/%E6%A4%9C%E7%B4%A2?q=%E3%83%86%E3%82%B9%E3%83%88");
decodeURIComponent("%E6%A4%9C%E7%B4%A2");
JavaScript

ざっくり対応はこうです。

  • encodeURI ↔ decodeURI
    → URL 全体を扱うペア
  • encodeURIComponent ↔ decodeURIComponent
    → URL の一部(パラメータ値など)を扱うペア

「エンコードに何を使ったか」と同じものをデコード側でも使う、これが一番大事なルールです。


decodeURIComponent で「パラメータ値」を元に戻す

クエリパラメータを自分でパースするイメージ

例えば、こんな URL があったとします。

/ search?q=JavaScript%20%E5%85%A5%E9%96%80

q の値を取り出して、元の文字列に戻したい。

const qs = "q=JavaScript%20%E5%85%A5%E9%96%80";

const [key, rawValue] = qs.split("=");
// key: "q"
// rawValue: "JavaScript%20%E5%85%A5%E9%96%80"

const value = decodeURIComponent(rawValue);
// "JavaScript 入門"
JavaScript

ここで重要なのは、rawValue に対して decodeURIComponent を使うことです。

エンコード側で

encodeURIComponent("JavaScript 入門");
JavaScript

を使っているので、デコード側も decodeURIComponent で対になる、というイメージです。

もし、ここで decodeURI を使うとどうなるか。

decodeURI("JavaScript%20%E5%85%A5%E9%96%80");
// "JavaScript 入門"(このケースではたまたま同じ結果)
JavaScript

この例ではたまたま同じですが、&= などが絡むと挙動が変わるので、
「パラメータ値には decodeURIComponent」と覚えておくのが安全です。


decodeURI で「URL 全体」を元に戻す

すでに URL として組み立てられたものを読む

例えば、ログにこんな URL が残っていたとします。

https://example.com/%E6%A4%9C%E7%B4%A2?q=%E3%83%86%E3%82%B9%E3%83%88

これを人間が読める形にしたいときは、decodeURI を使います。

const encodedUrl =
  "https://example.com/%E6%A4%9C%E7%B4%A2?q=%E3%83%86%E3%82%B9%E3%83%88";

const decodedUrl = decodeURI(encodedUrl);
// "https://example.com/検索?q=テスト"
JavaScript

ここで decodeURI がやっているのは、

  • %E6%A4%9C%E7%B4%A2検索
  • %E3%83%86%E3%82%B9%E3%83%88テスト
  • ただし、:/?= はそのまま

という変換です。

もし、ここで decodeURIComponent を使うとどうなるか。

decodeURIComponent(
  "https://example.com/%E6%A4%9C%E7%B4%A2?q=%E3%83%86%E3%82%B9%E3%83%88"
);
// "https://example.com/検索?q=テスト"
JavaScript

この例では同じ結果になりますが、
本来 decodeURIComponent は「URL 全体」ではなく「一部」に使うものなので、
「URL 全体を読むときは decodeURI」と覚えておくと混乱しにくいです。


実務でよくやる「クエリ文字列 → オブジェクト」ユーティリティ

location.search をパースして、値をデコードする

ブラウザで location.search を見ると、こんな文字列が取れます。

?q=JavaScript%20%E5%85%A5%E9%96%80&page=2

これを

{ q: "JavaScript 入門", page: "2" }
JavaScript

のようなオブジェクトにしたい、というのはよくある要件です。

function parseQueryString(qs) {
  const result = {};

  if (!qs) return result;

  const query = qs.startsWith("?") ? qs.slice(1) : qs;

  if (query === "") return result;

  const pairs = query.split("&");

  for (const pair of pairs) {
    if (!pair) continue;

    const [rawKey, rawValue = ""] = pair.split("=");

    const key = decodeURIComponent(rawKey);
    const value = decodeURIComponent(rawValue);

    result[key] = value;
  }

  return result;
}
JavaScript

ここでの重要ポイントは、

  • keyvaluedecodeURIComponent する
  • a=b=c のようなケースでも、最初の = だけで分割する(split("=") の最初の2つを使う)
  • ? を外してから処理する

というところです。

実際に使うとこうなります。

parseQueryString("?q=JavaScript%20%E5%85%A5%E9%96%80&page=2");
// { q: "JavaScript 入門", page: "2" }

parseQueryString("tag=A%26B");
// { tag: "A&B" }
JavaScript

エンコード側で encodeURIComponent を使っていれば、
デコード側で decodeURIComponent を使うことで、きれいに元に戻せます。


設計として意識してほしいこと

「エンコードとデコードは必ずペアで考える」

URL 周りのバグの多くは、

  • エンコードしていないのにデコードしようとする
  • encodeURI したものを decodeURIComponent で戻そうとする
  • そもそもどこでエンコードしたか分からない

といった「ペア関係の崩れ」から生まれます。

なので、

  • パラメータ値 → encodeURIComponent / decodeURIComponent
  • URL 全体 → encodeURI / decodeURI

という対応を、プロジェクトのルールとして決めておくと、かなり安定します。

「何でもかんでも decode しない」

受け取った文字列に対して、なんとなく decodeURIComponent をかけるのは危険です。

  • もともとエンコードされていない文字列に decode をかける
  • % の後ろが不正な形式でエラーになる

といったことが起きます。

「これは URL から来たクエリ文字列だから decode する」
「これはすでに人間が読む用の文字列だから decode しない」

という線引きを、コードの中で意識しておくと、バグを減らせます。


ちょっとだけ手を動かしてみる

コンソールで、次の順番で試してみてください。

decodeURIComponent("JavaScript%20%E5%85%A5%E9%96%80");
decodeURIComponent("A%26B");

decodeURI("https://example.com/%E6%A4%9C%E7%B4%A2?q=%E3%83%86%E3%82%B9%E3%83%88");

parseQueryString("?q=JavaScript%20%E5%85%A5%E9%96%80&page=2");
JavaScript

「どの部分がどう元に戻るか」
「エンコードされた %xx がどう文字に変わるか」

を、自分の目で確認してみてください。

その感覚がつかめると、
あなたの URL 周りのコードは、「なんとなく encode/decode を呼んでいる状態」から、
「どこでエンコードし、どこでデコードするかを意識した“業務レベルの URL ユーティリティ”」に変わっていきます。

タイトルとURLをコピーしました