開発者ツール · URL エンコーダーとデコーダー
scape() と encodeURIComponent: JavaScript の URL エンコーディングはどのように進化したか
· 背景
JavaScript URLエンコーディング 履歴
JavaScript には 3 世代にわたる URL エンコーディング関数があり、最も古いものはまだ製品コードに潜んでいます。この投稿では、escape() の何が間違っているのか、ES3 が URI 関数を追加した理由、そして URI 関数が保持される理由について説明します。 *'()。
レガシー ログ内の %u20AC — 紛れもないescape() の指紋と、それによって引き起こされるデコードの失敗
従来の JavaScript ファイルには、非推奨のエスケープ() 関数を使用した URL エンコーディング呼び出しが含まれています。ログ ファイルまたはエラー メッセージの出力には、シーケンス %u20AC が含まれます。これは、誰も使用していない非推奨のエスケープ() 関数の紛れもない指紋です。このシーケンスは標準の URL エンコーディングと一致しないため、RFC 3986 または WHATWG ルールに基づいて構築されたデコーダーはそれを認識できません。データは最新のツールを介して往復することはできません。これは、ES3 より前に存在し、1990 年代以降更新されていないコードの一般的な兆候です。
escape() 関数は、JavaScript の標準や正式な URL エンコード規則がなかった Netscape 時代に設計されました。ほとんどの非 ASCII 文字を、他では使われず標準にも定義されていない 4 桁の 16 進コードである %uXXXX 表記でエンコードします。ブラウザー内の一時的な用途には適していましたが、URL 標準との互換性を損ない、別の場所でデータをデコードできなくしました。
scape() と unescape(): Netscape 時代の設計 — ラテン語 1 の仮定、%uXXXX の発明、およびそれがどの標準にも一致しなかった理由
scape() および unescape() は、入力が Latin-1 (ISO 8859-1)、UTF-8 および Unicode より前の文字エンコーディングであると想定します。高ビットの Latin-1 文字には %XX を使用し、Latin-1 以外の文字には %uXXXX を使用して、各文字を 16 進コードに変換します。絵文字のような非ラテン文字 1 文字はまったく表現できません。関数はシンプルで高速ですが、現代のユースケースには完全に間違っています。
どちらの関数も、標準が存在する前に JavaScript に追加されました。これらは、ES3 が 1999 で適切な URL エンコーディングを導入した直後に非推奨になりました。これらは下位互換性のために JavaScript に残されており、削除すると古いコードが壊れてしまいます。ただし、新しいコードでは決してこれらを使用しないでください。それらは遺産です。
ES3 (1999) は encodeURI と encodeURIComponent を追加します — UTF-8 パーセントエンコーディングは RFC 2396 に準拠しています
ES3 では、encodeURI と encodeURIComponent という 2 つの関数が導入されました。どちらも UTF-8 パーセント エンコーディングを実行します。つまり、非 ASCII 文字を UTF-8 バイトに変換し、各バイトを %HH として書き込みます。どちらも当時最新の RFC 2396 に準拠しています。 RFC 3986 は後に登場しましたが、エンコーディングの動作は変更されませんでした。これらの機能は現在でも標準であるため、使用する必要があります。
encodeURI は、完全な URI をエンコードすることを目的としています。 encodeURIComponent は、クエリ値やパス セグメントなど、URI 内のコンポーネントをエンコードすることを目的としています。この違いは極めて重要であり、誤解されやすいものです。 encodeURI は : / ? のような構造文字を保持します。 # @ = & および ;。 encodeURIComponent はこれらすべてをエンコードし、より大きな URI 内に安全に埋め込めるようにします。
なぜ ! * ' ( ) はまだエンコードされていないままです — RFC 3986 が移動した後、RFC 2396 'マーク' 文字が言語に固定されました
どちらの関数も、文字、数字、ハイフン (-)、アンダースコア (_)、ピリオド (.)、チルダ (~)、および 5 つの句読点の文字をエンコードしないままにします。 *'()。マークは RFC 2396 に基づいており、予約されていない「マーク」文字としてリストされています。 RFC 3986 は 2005 で発表され、これら 5 つが別のカテゴリに移動されましたが、JavaScript はすでに 1999 で encodeURI と encodeURIComponent を凍結していました。そのまま残す文字を変更すると既存のコードが壊れてしまうため、そのままにしました。
下位互換性のためにこれら 5 つのマークをエンコードしないままにするという決定は、JavaScript のエンコードが RFC 3986 または WHATWG 標準のいずれにも完全には一致しないことを意味します。実用に十分近づいており、今から変更するのは全く不可能です。これは API の安定性に関する教訓です。一度動作を凍結すると、標準が進化しても変更することはできません。
作業例: エスケープ、encodeURI、および encodeURIComponent を使用した同じ文字列 — 3 つの出力の比較
文字列「R&D (研究) = カフェ's」を取得します。これを、escape()、encodeURI、および encodeURIComponent を通じて実行します。 scape() は、エンコードされていない括弧とアポストロフィを、パーセントでエンコードされたアンパサンドとイコールと混合して、「R%26D%20(research)%20%3D%20caf%E9's」を生成します。 encodeURI は、「R&D%20(research)%20=%20caf%C3%A9's」を生成します。アンパサンドと等号は構造的なものであるため、そのままにしておきます。 encodeURIComponent は「R%26D%20%28research%29%20%3D%20caf%C3%A9%27s」を生成し、括弧とアポストロフィを含むすべてをエンコードします。
同じ文字列を URL エンコーダーとデコーダーに貼り付け、encodeURI と encodeURIComponent を切り替えて違いを確認します。次に、escape() が何を生成するかを調べます (警告は表示されますが、ブラウザ コンソールで呼び出すこともできます)。 3 つの関数が 3 つのまったく異なる結果を生成することがすぐにわかります。
エスケープ() からの移行 - 古い呼び出しを適切な最新関数にマッピングし、保存された %uXXXX データを処理する
エスケープ() を使用する古いコードは更新する必要があります。 Escape() を使用して URI コンポーネントをエンコードした場合は、encodeURIComponent に置き換えます。完全な URI をエンコードするために使用された場合は、encodeURI を使用します。 %uXXXX シーケンスを含む保存データの場合は、カスタム デコーダが必要です。各 %uXXXX を Unicode コード ポイントに変換し、コード ポイントを文字列に収集します。 JavaScript の組み込み unescape() は %uXXXX を読み取りますが、結果は正しくない可能性があります UTF-8。
エスケープ() を置き換えた後、非 ASCII 文字、句読点、特殊文字を含む文字列を使用してコードをテストします。出力は、最新のツールや標準が期待するものと一致するはずです。コードが ES3 よりも大幅に古い場合は、他の古いパターンも使用されている可能性があります。包括的な監査は努力する価値があります。
これでカバーされないもの — URL および URLSearchParams API については個別に説明します
URL および URLSearchParams API は、かなり後になって追加され、URL の構築とコンポーネントのエンコーディングのための高レベルのインターフェイスを提供します。これらはすべてのエスケープを自動的に処理し、WHATWG URL 標準に正確に一致します。これらは、最新の JavaScript でプログラムによって URL を構築するための推奨される方法です。
この投稿では、エンコード関数のみを説明し、より高レベルの API については説明しません。 URL と URLSearchParams は構造を解析し、コンポーネント ルールを選択して結果をシリアル化します。一方、encodeURIComponent は、指定された 1 つの文字列をどこに配置されるかを知らずに変換します。その区別が境界です。値を処理するかアドレスを処理するかに応じて古いescape()呼び出しを移行し、その後、周囲の手動連結を別のリファクタとして構造化APIに置き換えることを検討してください。
要点: 3 つの関数、1 つの生き残ったペア — URL エンコーダーとデコーダーが最新の encodeURI と encodeURIComponent の動作を並べて表示する方法
最新の JavaScript 開発では、encodeURI または encodeURIComponent を使用する必要があり、決してエスケープ() を使用しないでください。関数は 1999 で標準化され、それ以来変更されていません。これらは、非 ASCII 文字を UTF-8 バイトとしてエンコードし、標準の予約文字を正しく処理します。 URL エンコーダおよびデコーダ ツールは両方の機能を実装しており、それらの動作を並べて確認できるため、コンポーネントに適したものを簡単に選択できます。
古いログまたは保存されたデータで %u シーケンスが見つかった場合、それらはエスケープ() 出力であるため、移行する必要があります。パターンを特定すれば、移行は簡単です。現代のコードでは決してそれらを生成すべきではありません。