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"
}
*/
host與hostname有微小的差異, 出現在含有 port 的時候- host 包含 domain name + port, 例如
localhost:8080 - hostname 僅包含 domain name, 例如
localhost
- host 包含 domain name + port, 例如
origin包含了 protocol + host, 比方說https://localhost:8080href意思是指hyper refer超連結, 就是原本的輸入search包含了?還有之後的Queryhash包含了#還有之後的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時, 相對路徑與絕對路徑的差異