URL の結合を完全に理解する
URL の結合をよく理解していなかったので、それに対する備忘録です。
例えば
<a href="/top">Top</a>
みたいな HTML の anchor タグを使う時、現在の URL と/top
を結合した URL に遷移するわけです。
これは、JavaScript の API にもあり、
const merged = new URL('/top', 'https://example.com/profile') // 'https://example.com/top'
のようにして結合することもできます。
この結合は Deno などの JavaScript ランタイムでファイルを参照する時にも使うのですが、 path.join
などとも挙動が違うのでまとめたいと思います。
URL の構造
JavaScript の URL
API によって、URL の各部分は以下のように分割できます。
これが URL の構造です。
この前提条件を踏まえて説明していきます。
また、new URL(next, base)
のようにして、next
を結合先、base
を結合元 と定義していきます。
/
結合先の最初が - 変化なし:
protocol
,username
,password
,hostname
,port
- 変化あり:
pathname
,search
結合先が/page
のような最初が/
で始まる場合です。
そのとき、結合元のpathname
以降が結合先で置き換わります。
例:
-
https://example.com
+/page
=https://example.com/page
-
http://example.com
+/page
=http://example.com/page
-
https://example.com/xxx
+/page
=https://example.com/page
-
https://example.com/xxx?x=y
+/page
=https://example.com/page
-
https://example.com
+/page?x=y
=https://example.com/page?x=y
/
で分割したものに..
が含まれる場合
結合先を- 変化なし:
protocol
,username
,password
,hostname
,port
- 変化あり:
pathname
,search
これは、パスを一層上に上がるイメージです。pathname
以降のみを変更します。
例えば、https://example.com/aaa/bbb/ccc?x=y
+ ../../../../../xxx
を考えてみます。
-
ccc
と..
が打ち消しあう。このとき、?x=y
は消える、
(https://example.com/aaa/bbb
+../../../../xxx
) -
bbb
と..
が打ち消しあう。
(https://example.com/aaa
+../../../xxx
) -
aaa
と..
が打ち消しあう。
(https://example.com/
+../../xxx
) -
..
は 空のpathname
について意味がないので、先頭の..
が消える
(https://example.com/
+xxx
) -
https://example.com/xxx
になる
みたいなイメージです。
/
で分割したものに.
が含まれる場合
結合先を- 変化なし:
protocol
,username
,password
,hostname
,port
- 変化あり:
pathname
,search
これは無視されます。
例えば、結合先が./a/b/c
の場合はa/b/c
と等価になります。
結合先が通常の文字列
- 変化なし:
protocol
,username
,password
,hostname
,port
- 変化あり:
pathname
,search
これの挙動を完全に理解していませんでした。
結合元を/
で分割した最後の部分以降を、結合先で置き換えます。
POSIX システムとは少し違うので注意です。
また、pathname
が何もないか、/
のみの場合、https://example.com/
+ aaa/bbb
= https://example.com/aaa/bbb
のようになります。
//
で始まる
結合先が- 変化なし:
protocol
- 変化あり:
username
,password
,hostname
,port
,pathname
,search
これは、プロトコルはそのままでそれ以外を結合先で置き換えます。
例:
-
https://example.com/path
+//example.net/
=https://example.net/
-
http://example.com/path
+//example.net/
=http://example.net/
同じプロトコルで <script>
を使いたい時などに使えます。
結合先が完全な URL である
- 変化なし: なし
- 変化あり:
protocol
,username
,password
,hostname
,port
,pathname
,search
この場合、結合元の全てを結合先で置き換えます。
例:
-
https://example.com/path
+http://example.net/
=http://example.net/
まとめ
ファイルパスの操作などで URL API を使ったりする時は、path.join
と挙動が違うので、気をつけたほうがいいです。
Discussion