Node.js 透過全域的 URL 類別來解析絕對的 HTTP 與 HTTPS URL,該類別實作了 WHATWG URL 標準——也就是每個現代瀏覽器中 URL 建構函式所遵循的同一份規格。傳入像 new URL("https://example.com/docs/page?id=42#top") 這樣的字串時,會回傳一個物件,其中包含經標準化的 protocol、host、hostname、port、pathname、search 與 hash 欄位,外加一個對應的 URLSearchParams 視圖來呈現查詢字串。由於 Node.js 10 以上版本把 WHATWG URL 公開為全域物件,現代 API 並不需要 require('url'),而且你在終端機得到的輸出會與瀏覽器中 document.createElement('a') 所產生的結果一致。正是這種對等性,讓像 URL Parser 這樣的瀏覽器端檢視工具,成為在撰寫腳本之前快速原型設計與稽核解析結果的方法。這個工具會使用瀏覽器的 URL 建構函式(不帶 base URL)執行、將 scheme 與主機大小寫標準化、移除預設埠號,並在不解碼的情況下序列化經百分號編碼的路徑位元組,因此你所看到的每個欄位,都和 WHATWG 解析器在 Node.js 腳本中會回傳的結果一模一樣。

how to parse url in node js
如何使用 Node.js 內建的 URL 類別解析 URL

為什麼 Node.js 的 URL 類別與瀏覽器一致

Node.js 開發者可以預期一個解析後的 URL 物件,行為會與在 Chrome、Firefox 或 Safari 中建立的物件相同,因為這兩種環境都以 WHATWG URL 標準為目標,而不是各自獨立的舊版實作。因此 host、hostname 與 port 這些欄位無論程式碼在哪裡執行,都遵循同一套定義,百分號編碼的位元組也會經由同一個解析器進行標準化。這個標準以 WHATWG URL 規格的形式發佈,並記載於 MDN URL 參考頁面上,這也是任何想查詢邊角情況(例如反斜線處理、IDN 序列化、或預設埠號移除)的 Node.js 開發者所依據的權威參考。當你把 URL 貼進一個以相同建構函式打造的瀏覽器檢視工具時,其結果會反映你的 Node.js 程式碼會印出的內容,因此你可以反覆調整輸入,而不必每次都重新執行腳本並等待 console.log 輸出。

解析器會揭露的 URL 組成部分

這個工具會顯示一個典型的 Node.js 或瀏覽器腳本會從已解析 URL 物件中讀取的所有元件,另外再加上少數對交接與稽核很有用的衍生欄位。完整的清單取自 WHATWG URL 物件加上 URL Parser 實作,看起來像這樣:

欄位內容說明
href經 WHATWG 標準化後完整序列化的 URL
originscheme、hostname,以及實際生效的非預設埠號
protocol一律以冒號結尾,例如 https:
host當埠號為非預設值時,會包含 hostname 加上埠號
hostnameASCII 主機名;國際化名稱會以 xn-- 標籤呈現
port若為該 scheme 的預設埠號則為空,否則為明確的數字
pathname保留百分號跳脫字元的序列化路徑
search包含前導 ? 的查詢字串,可能為空
hash包含前導 # 的片段識別碼,可能為空
filename最後一個斜線之後的路徑區段;當路徑以 / 結尾時為空
query parameters來自 URLSearchParams 的有序陣列,重複的名稱會保留下來

最常讓開發者措手不及的唯一規則是預設埠號的移除:在 HTTPS URL 上明確加上 :443 會產生一個空的 port 欄位,且 host 不會帶有該後綴,因為 WHATWG 解析器會將其視為該 scheme 的預設值。像 :8080 這種非預設埠號則會保留在 host 中,也會另外出現在 port,因此這兩個欄位在這種情況下並非冗餘。以中括號包住的 IPv6 主機會保留其中括號序列化的 hostname,而當存在非預設埠號時,host 會再附加該埠號。

三個步驟解析一個 URL

  1. 把一個絕對的 HTTP 或 HTTPS URL 貼到輸入框中。字元數會在你輸入時更新,且輸入上限恰好為 8,192 個 UTF-16 字碼單元——超過時下一個字元會被拒絕,而非截斷。
  2. 選擇 Parse URL。此工具會在不帶 base 的情況下執行瀏覽器的 URL 建構函式,驗證 scheme 是否為 http 或 https,並在產生結果之前拒絕任何非空的使用者名稱或密碼。輸出面板接著會顯示經標準化的 href、origin、protocol、host、hostname、port、pathname、search、hash、filename,以及由 URLSearchParams 解碼的有序查詢參數。
  3. 檢查查詢字串與雜湊片段中是否含有任何機密資料,若可以安全分享,再複製完整的解析 JSON。編輯輸入會清除先前的結果、錯誤、查詢清單、摘要、輸出與剪貼簿狀態,因此失敗的 URL 絕不會讓先前的成功結果殘留在畫面上。

解析器在建構結果之前會拒絕的內容

此工具刻意設計得很嚴格,因此絕不會把不相類的輸入當作共用同一個模型來呈現。以下輸入會在邊界處被拒絕,並產生錯誤而非解析物件:

  • 空輸入
  • 長度超過 8,192 個 UTF-16 字碼單元的輸入
  • ASCII 控制字元,包括某些編輯器會悄悄移除的貼上換行字元與定位字元
  • URL 前後包含空白字元
  • 原始的反斜線——某些舊版程式庫會將其視為路徑分隔符號
  • 任何不以不分大小寫、字面意義的 http:// 或 https:// 前綴開頭的內容
  • http 與 https 以外的 scheme——即使瀏覽器的 URL API 能解析它們,這包括 javascript:、data:、file:、ftp:、blob: 與 mailto:
  • 像 /docs/page 這樣的相對路徑、像 //example.com/path 這類 scheme 相對參照,以及像 example.com 這類裸網域——因為此建構函式是在沒有 base URL 的情況下呼叫的,否則這些將會相對於目前頁面進行解析
  • 任何含有非空使用者名稱或密碼的 URL——例如 https://user:[email protected] 會回傳憑證錯誤且不產生任何解析欄位,因為此工具絕不顯示或部分遮罩 userinfo
  • 查詢字串項目超過 200 個、格式化後的 JSON 輸出超過 50,000 個字碼單元,或任何會使結果超出上述精確上限的輸入

這些限制意味著單一失敗的 URL 不會留下殘留狀態。編輯輸入會移除舊的結果、錯誤、查詢清單、摘要、輸出與剪貼簿狀態,重新執行時會從一個清空的狀態開始。剪貼簿寫入受到世代保護(generation-guarded),因此當輸入已經變更時,正在進行中的複製承諾無法發布誤導性的「Copied」狀態。

URL Parser 何時能幫你省下 Node.js 的來回測試

有幾項除錯工作通常需要在 Node.js REPL 或快速腳本中完成,現在只需要貼上並點擊一下:

  • 確認預設埠號的標準化。貼上 https://api.example.com:443/v1,確認 port 欄位為空,且 host 不再包含 :443。
  • 檢查 IDN 主機名。貼上一個 Unicode 國際化網域名稱,從 hostname 欄位讀取瀏覽器的 xn-- 序列化形式,這就是你的 Node.js 程式碼會用來比對的形式。
  • 稽核重複的查詢參數。像 ?tag=a&tag=b 這樣的 URL 會產生一個保留兩個項目的有序陣列。此解析器不會把重複的鍵合併成物件,因此像 tag 這種具有兩個值的鍵會以伺服器實際接收的樣貌完整呈現。
  • 區分路徑、查詢與片段文字。pathname、search 與 hash 欄位會各自獨立顯示,並保留前導的 ? 與 #,這在手工重建一個 URL 時相當有用。
  • 看出百分號編碼的差異。pathname 與 filename 會保留像 %20 與 %2E 這類的百分號跳脫字元,而查詢參數的名稱與值則由 URLSearchParams 解碼,包括加號轉空格的轉換。用肉眼確認這個區分,比閱讀規格文件更快。

有幾項工作這個工具並非為此而設計。它不會去瀏覽這個 URL、不會去抓取它、不會跟隨重新導向、不會掃描惡意程式、不會測試 DNS、不會驗證 TLS、不會解碼任意的二進位百分號序列,也不會移除追蹤參數。把它視為對輸入字串的檢視工具,而不是對目的地的檢查。對於相關的工作,將查詢字串解析為 JSON 涵蓋了更廣泛的查詢處理工作流程,而 MDN URL 參考文件 則記載了此工具所讀取的每個欄位。

由於 Node.js 的 URL 類別與瀏覽器的 URL 建構函式都實作了 WHATWG URL 標準,URL Parser 所顯示的解析欄位,與你在腳本中呼叫 new URL(...) 會得到的欄位完全相同。這讓此工具成為把解析器提交到程式碼庫之前的有用預先檢查,也是在不開啟終端機的情況下,向同事展示 URL 解析的快速方式。

延伸閱讀:以更安全的複製習慣為核心打造的 Git 小抄替代方案。