iimon TECH BLOG

iimonエンジニアが得られた経験や知識を共有して世の中をイイモンにしていくためのブログです

URLのエンコードについて

こんにちは、iimonフロントエンジニアの齋藤です。 本記事はiimonアドベントカレンダー5日目の記事となります。

はじめに

業務でurlを扱う際にコード上で長くなりすぎてしまったため、コードの可読性を高めるために改行をいれたところ、url上にスペースを表す%20が大量に入ってしまいました。

URLを作成する際に改行を含めると、それが %20 にエンコードされてしまうためです。 この%20問題でURLの扱いについて考えるようになりました。

コード例

'https://www.hoge.jp/hoge1?aaaaaaaa&
hoge2?aaaaaaaaaaaaaaaaaaaaaaaaaaa'

実際のURL

https://www.hoge.jp/hoge1?aaaaaaaa&%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20hoge2?aaaaaaaaaaaaaaaaaaaaaaaaaaa

ちなみにこの例では+ 演算子を使用して2つの文字列を連結して解決しました。この方法では、改行は文字列の一部として扱われず、結果のURLに %20 は含まれません。

'https://www.hoge.jp/hoge1?aaaaaaaa&'+
'hoge2?aaaaaaaaaaaaaaaaaaaaaaaaaaa'

URLエンコードとはなにか

URL内の特定の文字を異なる表現に変換するプロセスです。URLには特定の文字(スペース、記号、日本語の文字など)が含まれることがありますが、これらの文字はURLとして直接扱うことができません。そのため「%」の後ろに使用できない文字の文字コードを16進数で表したものを連結します。(パーセントエンコーディング)

これによりURL内の特殊な意味を持つ文字を扱う際の問題を回避します。

urlで使えない文字:

  • 日本語や他の多言語の文字(非ASCII文字)
  • スペース
  • 予約文字

→区切り文字などの特定目的で用いるために予約されている文字で、その目的以外ではURLに使用できません。

例:!、*、'、(、)、;、:、@、&、=、+、$、,、?、/、#、[、]。

エンコードはしないといけないのか

エンコードは、URL内での文字列の正確な表現を保証し、意図しないエラーや問題を回避するために重要です。しかし、一般的な文字列(ASCII文字など)を含む簡単なURLを扱う場合、エンコードを行わずにそのまま使うことも可能です。

ただし、URL内に特定の予約文字(?, &, =, +, # など)が含まれる場合は、エンコードしないとURLの意味が壊れてしまい、サーバーエラーなどを引き起こす可能性があるのでURLを扱うときはエンコードをしたほうがよいでしょう。

エンコードの手法について

javascriptエンコード方法についてはいくつかあります。 以下で説明していきます。

encodeURI

encodeURI(URI)

引数にはURIの完全な文字列を指定します。

文字列の中の特別な文字がエンコードされた新しい文字列が戻り値として返されます。

なお、以下の文字はエンコードされません。

英数字 - _ . ! ~ * ' ( ) # ; , / ? : @ & = + $

これらはURI内で特別な意味を持つ予約文字であり、URIとしての構造を決定するため、そのまま残されます。

たとえば、URIでの? (クエリ文字)はURI内でクエリパラメーターを示すために使用される特殊文字になるため、もしエンコードされてしまうとURIの崩壊を招く可能性があります

実際に使ってみる 

let uri = 'https://www.example.com/ほげ path with spaces/index.html?key=value&otherKey=otherValue#section';
let encodedURI = encodeURI(uri);

実際のURLは↓みたいな感じになります

https://www.example.com/%E3%81%BB%E3%81%92%20path%20with%20spaces/index.html?key=value&otherKey=otherValue#section

日本語のほげ やスペースはエンコードされて、

英数字のpathwithspaces特殊文字である?#=エンコードされていません

問題点:

パラメータの値に & などの予約文字が含まれている場合には問題となります。

let uri = 'https://www.example.com/index.html?hoge=value1&value2'
let encodedURI = encodeURI(uri);

実際のURL 

https://www.example.com/index.html?hoge=value1&value2

これはhoge をキーとして値をvalue1&value2 としたいが、実際には

&エンコードされず、URL上での役割であるクエリパラメータを区切ると

いう役割となり、正しい値になりません。

このようなパラメーターの値に特殊文字が含まれてるときのエンコード方法については

次で説明するencodeURIComponentを使うのが適切です

encodeURIComponent

encodeURIComponent(str)

引数には対象となる文字列を指定します。

これによりURI内で特別な意味を持つ文字や、URI構造を壊す可能性のある文字をエンコードできるようになります。

先程のencodeURIを用いて、うまくエンコードできなかった以下のURLをencodeURIComponentを使ってみる

https://www.example.com/index.html?hoge=value1&value2

実際に使ってみる

`https://www.example.com/index.html?hoge=${encodeURIComponent("value1&value2")}`;

実際のURL

https://www.example.com/index.html?hoge=value1%26value2

先程のencodeURIでは、&エンコードされていませんでしたが、encodeURIComponentを使用することでうまくエンコードできるようになり、正しい値になりました。

注意点:

encodeURIComponent の引数にURL全体の文字列を入れてしまうと、以下のようにエンコードしてしまうので注意が必要です。:/ エンコードしてしまっています

let uri = 'https://www.example.com/index.html?hoge=value1&value2'
let encodedURI = encodeURIComponent(uri);

実際のURL

https%3A%2F%2Fwww.example.com%2Findex.html%3Fhoge%3Dvalue1%26value2:

URI全体をエンコードする必要がある場合には encodeURI() を使い、URIコンポーネント(urlの各部分)をエンコードする場合には encodeURIComponent() を使います。

URLSearchParams

URLにパラメーターを設定するとき、個別にencodeURIComponent()encodeURI()を個別に実行する必要があります。パラメーターが多いときはコードも読みにくくなります。そんなときはURLSearchParamsを使うと便利です。

const hoge1 = encodeURIComponent("value1");
const hoge2 = encodeURIComponent("value2");
const sampleURL = `https://sample.com?key1=${hoge1}&key2=${hoge2}`;

URLSearchParams を使うと、set()を使って設定した各キーと値が、URIで適切にエンコードされます。これにより、個別に encodeURIComponent() を実行する手間を省くことができます。URI内で特別な意味を持つ文字が安全にエンコードされ、正しい形式のクエリストリングが作成されます。

const param = new URLSearchParams();
param.set("key1", "value1");
param.set("key2", "value2");
const sampleUrl = `https://sample.com?${param.toString()}`;

他にもパラメータを編集、取得、削除することができ、URIコンポーネントエンコードを意識する必要なく操作できます。

参照したサイト

https://zenn.dev/megeton/articles/5f1ba5c7e1bfd0

最後に

この記事を読んで興味を持って下さった方がいらっしゃればカジュアルにお話させていただきたく、是非ご応募をお願いします。

Wantedly / Green

次のアドベントカレンダーの記事はUber Eatsヘビーユーザーのほでぃです。 自炊する時間を惜しんでまで書く記事は必見です!!