URL

在討論 URL 之前, 先來看常見的三個定義:

  • URI - Uniform Resource Identifier, 意指用於標識某一網際網路資源名稱的字串
  • URL - Uniform Resource Locator, 俗稱網頁位址, 簡稱網址, 是網際網路上標準的資源的位址
  • URN - Uniform Resource Name, 期望為資源提供持久的、位置無關的標識方式

大概可以理解為 URI = URL + URN, 這裡不深入探討三者的差異

但是以現今的用法來說, URI 可以看作和 URL 等價, URN 則是獨立的用法

URL 的形式

URL 通常會表現為如下的形式:

                    hierarchical part
        ┌───────────────────┴─────────────────────┐
                    authority               path
        ┌───────────────┴───────────────┐┌───┴────┐
  abc://username:[email protected]:123/path/data?key=value&key2=value2#fragid1
  └┬┘   └───────┬───────┘ └────┬────┘ └┬┘           └─────────┬─────────┘ └──┬──┘
scheme  user information     host     port                  query         fragment

在該例子中:

  • abc 是協定名稱
  • username 是用戶
  • password 是密碼
  • example.com 是網域
  • 123 是連線的 port
  • /path/data 是路徑
  • ?key=value&key2=vale2 是查詢字串
  • fragid1 是 fragment

而常見的

https://www.google.com

若未指定通訊埠(之後都稱 port ), http 使用 80 port, https 使用 443 port

請注意, URL 不一定只用在 HTTP 協議上, 比方說 mysql 或是 postgresql 可以使用 URL 的方式登入

  • 用戶名稱為 Nero
  • 密碼為 a1234qwer
  • 伺服器位於 test.database.cc 所指向的 IP
  • 使用 5432 port
  • 資料庫名稱為 mydatabase

那個可以使用

postgresql://Nero:[email protected]:5432/mydatabase

或是加上 Query 參數, 設定額外的資訊, 如

postgresql://Nero:[email protected]:5432/mydatabase?connect_timeout=10&application_name=myapp

而 username 和 password, 實務上比較少用到, 但是如果你想嘗試的話, 可以透過這種方法驗證:

  • 在 github 或是 gitlab 上開啟 Repo, 並提交 initial commit
  • git clone 該 repo
  • git push, 此時終端機會請你輸入帳號、密碼
  • 中止上個階段
  • 使用指令 git remote set-url origin https://[username]:[password]@github.com/<repo>
  • 再次嘗試 push, 此時不必輸入帳號、密碼就可以完成驗證

跟 URL 有關的 API

在瀏覽器中, 有個URL物件, 可以用來解析基於 HTTPS 和 HTTP 的 URL

const str = "https://abc:[email protected]?key=1&v=2#test"
const url = new URL(str); /* 
{
  hash: "#test"
  host: "dev.yhchen.monster"
  hostname: "dev.yhchen.monster"
  href: "https://abc:[email protected]/?key=1&v=2#test"
  origin: "https://dev.yhchen.monster"
  password: "passwd"
  pathname: "/"
  port: ""
  protocol: "https:"
  search: "?key=1&v=2"
  searchParams: URLSearchParams {}
  username: "abc"
}
*/
  • hosthostname 有微小的差異, 出現在含有 port 的時候
    • host 包含 domain name + port, 例如 localhost:8080
    • hostname 僅包含 domain name, 例如 localhost
  • origin 包含了 protocol + host, 比方說 https://localhost:8080
  • href 意思是指 hyper refer超連結, 就是原本的輸入
  • search 包含了 ? 還有之後的Query
  • hash 包含了 # 還有之後的Fragment

通常來說, 查詢字串的形式為key=value, 並透過& 連結起來

可以用複雜一點的作法來 parse 查詢字串:

const str = "https://dev.yhchen.monster?key=1&v=2&g=25&k=40"
const url = new URL(str);
const query = url.search;
const entries = query.slice(1).split('&').map(token => token.split('='))
const queryObj = Object.fromEntries(entries);

不過各位學員其實可以注意到, URL物件有提供 searchParams 這個物件, 他提供了一些實用的方法

假設現在有個複雜一點的 Query String:

?key=1&v=2&g=25&k=40&qe=hello

透過以下的方法:


/* ... parse 結束 */
const params = URL.searchParams;

/* 迭代器家族 */
[...params.keys()] // result: ['key', 'v', 'g', 'k', 'qe']
[...params.values()] // result: ['1', '2', '25', '40', 'hello']
[...params.entries()] /* result: 
  [
      ["key","1"],
      ["v","2"],
      ["g","25"],
      ["k","40"],
      ["qe","hello"]
  ]
*/

/* 設值器 */
params.append('test', 'string');
// url.search: "?key=1&v=2&g=25&k=40&qe=hello&test=string"

params.append('test', 'test');
// url.search: "?key=1&v=2&g=25&k=40&qe=hello&test=string&test=test"

/* 取值器 */
params.get('key') // result: 1
params.getAll('test') // result: ['string', 'test']
params.get('test') // 取出第一個 result: 'string'

/* 覆蓋 & 刪除 */
params.set('test', '0')
// url.search: "?key=1&v=2&g=25&k=40&qe=hello&test=0"

params.delete('test')
// url.search: "?key=1&v=2&g=25&k=40&qe=hello"

同時, 可以透過 location.href 取得目前的網址

至於 Fragment, 是比較特殊的用法, 在之後幾個章節在說明

相對與絕對路徑

之前有提到過, 會透過 fetch API 抓取遠端的資料, 並且假定目前的網址為

https://www.example.com/docs/page/URL-intro

而 API 的端點位於 https://www.example.com/api/datas

fetch("api/datas") // Errors

fetch("/api/datas") // It Works!

原因在於, 一開始的/ 會改變 URL 的參照, 使用 / 發出的請求, 會以 origin 作為 Base

而沒有使用 /, 則是以當前路徑作為參照點:

// Current Location: https://www.example.com/docs/page/URL-intro

fetch("api/datas") // 請求的URL為 https://www.example.com/docs/page/api/datas

fetch("/api/datas") // 請求的URL為 https://www.example.com/api/datas

這是一個需要注意的地方

章節回顧

該章節僅需要知道 URL 的構成語法, 以及瀏覽器中與 URL 有關的 API 即可

  • URL 的組成
  • URL 物件與 location.href
  • 發出Request時, 相對路徑與絕對路徑的差異