開発者ツール · URL エンコーダーとデコーダー
クエリ文字列をデコードするときに + がスペースになるのとそうでない場合に + がスペースになるのはなぜですか
· 仕組み
URLエンコーディング JavaScript 開発者ワークフロー
+ がスペースを意味するかどうかは、呼び出すデコーダによって異なります。この投稿では、decodeURIComponent、URLSearchParams、およびサーバー フレームワークがそれぞれ + をどのように扱うか、および実際のプラスがスペースに変換されないようにする方法について説明します。
クエリ文字列をデコードするときに + がスペースになるのとそうでない場合に + がスペースになるのはなぜですか
HTML フォームの送信では、スペースがプラスになる application/x-www-form-urlencoded 形式を使用します。 name=Alice+Smith を受信するサーバーは、値を抽出する前に、各プラスをスペースに置き換えます。計算 5+3 のように、実数のプラスがデータに属している場合、フォーム デコード ステップの後、5 3 としてサーバーに到着します。この目に見えない変換が混乱の根源です。
JavaScript デコードでは、使用する関数に応じて異なる結果が生成されます。 URLSearchParams は、サーバーの動作に合わせてプラスをスペースとして扱います。ただし、decodeURIComponent は plus をそのままにし、文字通りに扱います。関数間のこの非対称性が、同じ入力が異なる方法でデコードされる理由です。両方のデコーダが同じ結果を生成することを期待していた開発者は、そうではないことに気づきます。
似ている 2 つのエンコーディング — RFC 3986 パーセント エンコーディングとアプリケーション/x-www-form-urlencoded
2 つのエンコード標準は似ていますが、動作は異なります。 RFC 3986 はパーセント エンコーディングを定義しており、任意の文字は %HH になります。スペースは %20 になります。 application/x-www-form-urlencoded 標準では、スペースをプラスにできるという省略表現が追加されています。どちらもフォーム コンテキストで機能しますが、plus はオプションであり、その標準に固有です。これらは、類似した外観を持つ異なるドメインです。
decodeURIComponent を呼び出すと、RFC 3986 デコードのみが適用されます。 %20 はスペースとして読み取られ、プラスはリテラルのプラスとして読み取られます。 URLSearchParams はフォーム デコード ルールを適用します。つまり、パーセント エスケープはその文字になり、プラスはスペースになります。 2 つの関数は、異なるドメインで同じ問題を解決します。これらを混合すると、実数のプラスが消えるか、スペースがプラスになって変換できなくなります。
decodeURIComponent は + をそのままにします。 URLSearchParams はスペースに変換します - 2 つの JavaScript 動作の比較
サーバーの動作はさまざまであり、それが問題を複雑にします。 Rails または Django は、プラスがスペースになるというフォーム ルールを自動的に適用します。ただし、URL デコーダーを使用して生のクエリ文字列を抽出して手動でデコードすると、plus はそのまま残ります。同じ値を異なるフレームワークで処理すると、異なる結果が生成されます。多くの場合、サーバー コードはこれを暗黙的に処理し、カスタム デコーダを作成するまで問題を隠します。
例: 電話番号フィールドには、国コードとして +1-555-0100 が格納されます。 JavaScript は %2B としてエンコードされるため、HTML フォームはこれを %2B1-555-0100 としてエンコードします。サーバーはこれを受け取ります。フォームデコードを適用すると、%2Bがプラスとなり、正しい値となります。プロキシがエンコーディングを削除する場合、結果に対して decodeURIComponent を呼び出すと、+1-555-0100 が生成されます。各レイヤーは 1 回デコードします。
サーバーの動作 — クエリ文字列とリクエスト本文における一般的なフレームワークの動作 (一般的な用語で説明)
JavaScript は encodeURIComponent を使用して値をエンコードできます。 a+b を指定すると、a%2Bb が生成されます。そのエンコードされた文字列がサーバーまたはフォーム対応デコーダーに到達すると、%2B は plus としてデコードされ、結果は正しいものになります。代わりに形式ルールを使用してエンコードすると、スペースはプラスになり、実数のプラスは %2B になります。いずれにせよ、エンコードにより %2Bb が生成されます。解釈は、どのデコード規則が適用されるかによって異なります。
往復をテストします: a+b から始めます。 encodeURIComponent でエンコードして、%2Bb を取得します。 decodeURIComponent で a%2Bb をデコードし、a+b を復元します。 a+b を URLSearchParams に渡します。プラスはスペースとして扱われ、b が生成されます。 a%2Bb を URLSearchParams に渡して、a+b を返します。同じ入力を 2 つの方法でデコードすると、使用するデコーダに応じて異なる出力が生成されます。
作業例: 両方のデコーダーを介した 'a+b' と 'a%2Bb' — テーブル内の 4 つの結果
よくある間違いがすぐ続きます。開発者は decodeURIComponent を使用してデコードし、実際のプラスを持つ受信フォーム データがなぜ壊れるのか疑問に思いました。 URLSearchParams を使用する必要がありました。逆に、decodeURIComponent を使用する必要があるときに URLSearchParams を使用すると、リテラルのプラスがすべて消えてしまいます。 2 回エンコードすると %252B が生成され、正しくデコードするにはエンコーダとデコーダのペアが一致する必要があります。
もう 1 つの間違いは、エンコードせずに手動でクエリ文字列を ?q=value として構築することです。値にアンパサンドまたは等号を指定すると、新しいパラメータが自動的に作成されます。ブラウザは連結を後から推測しません。結果は正しく形成されたものとして扱われます。これを防ぐには、encodeURIComponent を使用した意図的なエンコードのみが必要です。 URL エンコーダーとデコーダーは 3 つの関数すべてを示し、それぞれが何を生成するかを明らかにします。
よくある間違い — パス セグメント内のスペースを 2 回デコードするか、スペースを + としてエンコードする
フォーム エンコード ルールは、HTTP リクエスト本文の Content-Type ヘッダーを記述するため、application/x-www-form-urlencoded と呼ばれます。ファイルをアップロードしない HTML フォームは、本文をこの形式で送信します。 URL 内のクエリ文字列もこの規則を使用しますが、技術的には公式のエンコード標準はありません。 URL 仕様ではクエリを不透明として扱います。プラスの意味は必須ではありません。ただし、Web アプリケーションでは、プラスは通常スペースを意味します。
正しい動作を保証するには、意図的にエンコードし、マッチング関数を使用してデコードします。 encodeURIComponent でエンコードした場合は、decodeURIComponent でデコードします。 HTML フォーム データまたはリクエスト本文をフォーム形式で読み取る場合は、URLSearchParams を使用します。決して外見に基づいて推測しないでください。 a+b のような文字列は曖昧です。デコーダは互換性がありません。
これでカバーされないもの — マルチパート フォーム データと JSON リクエスト本文
マルチパート フォーム データ、JSON リクエスト本文、およびその他の標準には個別のエンコード ルールがあります。 JSON は、スペースまたはパーセントエンコーディングにプラスを使用しません。 Unicode エスケープを使用します。マルチパートでは異なる境界が使用されます。この記事では、クエリ文字列とフォーム エンコードされた本文についてのみ説明します。これは、そこに曖昧さが現れるためです。 Content-Type ヘッダーとそれを定義する RFC を必ず確認してください。
リテラル プラスがクエリ値に含まれる場合は、常に %2B としてエンコードします。 URL エンコーダとデコーダは、%20 となるスペースとは別に、コンポーネント モードで plus が %2B として保護される方法を示します。 a+b と a%2Bb を各モードに渡し、結果を調べます。この比較により、同じ入力が異なる方法でデコードされる理由がわかります。違いは、2 つの異なる標準の正しい動作です。
要点: リテラル プラスは常に %2B としてエンコードする — URL エンコーダーとデコーダーが、パーセントでエンコードされたクエリ値として値がどのように見えるかを示す方法
要点: クエリ文字列内のプラスは、%2B として保護されているエンコーディングからのものでない限り、リテラルのプラスではなく、スペースの形式エンコーディング短縮形です。間違ったデコーダはその保護を失います。 URLSearchParams は最新の JavaScript では最も安全です。フォームのエンコーディングを処理し、名前付きパラメーターへのアクセスを提供します。生の文字列の場合、encodeURIComponent はすべてを保護します。 decodeURIComponent は %20 とパーセントを解釈しますが、プラスは文字通りに扱います。
これをテストします: ?x=a+b を手動で構築し、URL エンコーダーとデコーダーに貼り付けます。これを調べて、URLSearchParams が値 a b を持つパラメーター x に分割することを確認します。 ?x=a%2Bb を貼り付けると、値 a+b が表示されます。 encodeURIComponent を使用して URL を構築し、比較します。この視覚的な確認により、ルールが明確になります。フォーム ルールでは plus が使用され、パーセント エンコーディングでは %20 が使用されます。これらを混合すると、plus が空間に消えてしまうのです。