URL 解析與編碼完全指南:從結構拆解到安全處理的全流程實戰

加密編碼編碼、雜湊與 QR Code

URL 的完整結構

https://user:pass@example.com:8080/path/to/page?name=value&age=25#section
  │       │     │    │          │  │   │            │                │
  scheme  user  pass  host      port path           query            fragment
組成部分 範例 說明
scheme https 協定
userinfo user:pass 認證資訊(已很少使用)
host example.com 網域或 IP
port 8080 連接埠號(預設 80/443)
path /path/to/page 路徑
query ?name=value&age=25 查詢參數
fragment #section 錨點(不發送到伺服器)

使用 URL API 解析

基礎解析

const url = new URL('https://example.com:8080/path?q=test#section');

url.protocol  // "https:"
url.hostname  // "example.com"
url.port      // "8080"
url.pathname  // "/path"
url.search    // "?q=test"
url.hash      // "#section"
url.origin    // "https://example.com:8080"
url.href      // 完整 URL

解析查詢參數

const url = new URL('https://example.com/api?name=Alice&age=25&tags=js&tags=css');

// URLSearchParams
url.searchParams.get('name')     // "Alice"
url.searchParams.get('age')      // "25"
url.searchParams.getAll('tags')  // ["js", "css"]
url.searchParams.has('name')     // true
url.searchParams.delete('age')

// 遍歷
for (const [key, value] of url.searchParams) {
  console.log(`${key} = ${value}`);
}

修改 URL

const url = new URL('https://example.com/page');

url.pathname = '/new-page';
url.searchParams.set('sort', 'desc');
url.searchParams.append('filter', 'active');
url.hash = '#top';

console.log(url.toString());
// "https://example.com/new-page?sort=desc&filter=active#top"

URL 編碼規則

為什麼需要編碼?

URL 只允許 ASCII 字元,其他字元必須編碼:

原始:https://example.com/search?q=你好 世界
編碼:https://example.com/search?q=%E4%BD%A0%E5%A5%BD%20%E4%B8%96%E7%95%8C

encodeURI vs encodeURIComponent

函式 編碼範圍 用途
encodeURI 保留 URL 結構字元(:/?#[]@!$&'()*+,;=) 編碼完整 URL
encodeURIComponent 編碼所有非 ASCII + 保留字元 編碼查詢參數值
const url = 'https://example.com/path?name=你好&redirect=/home';

// encodeURI:保留 URL 結構
encodeURI(url);
// "https://example.com/path?name=%E4%BD%A0%E5%A5%BD&redirect=/home"

// encodeURIComponent:編碼所有特殊字元
encodeURIComponent(url);
// "https%3A%2F%2Fexample.com%2Fpath%3Fname%3D%E4%BD%A0%E5%A5%BD%26redirect%3D%2Fhome"

// ✅ 正確用法:參數值用 encodeURIComponent
const name = '你好 世界';
const redirect = '/home';
const fullUrl = `https://example.com/path?name=${encodeURIComponent(name)}&redirect=${encodeURIComponent(redirect)}`;

常見編碼對照

字元 編碼 說明
空格 %20 URL 中不用 +
+ %2B 查詢參數中 + 會被解碼為空格
& %26 查詢參數分隔符
= %3D 鍵值分隔符
# %23 錨點識別符
% %25 編碼前綴本身
中文 %E4%BD%A0 UTF-8 三位元組編碼

使用工具庫編解碼 URL

  1. 開啟 URL 編解碼工具
  2. 在輸入框貼上 URL 或文字
  3. 點擊「編碼」或「解碼」
  4. 查看結果

URL 安全處理

1. 防止 URL 注入

// ❌ 危險:直接拼接使用者輸入
const url = `/api/users/${userId}`;

// ✅ 安全:驗證 + 編碼
function buildUserUrl(userId) {
  if (!/^\d+$/.test(userId)) {
    throw new Error('Invalid user ID');
  }
  return `/api/users/${encodeURIComponent(userId)}`;
}

2. 防止開放重新導向

// ❌ 危險:使用者可以指定任意 redirect URL
app.get('/login', (req, res) => {
  res.redirect(req.query.redirect);
});

// ✅ 安全:白名單驗證
function safeRedirect(url, allowedHosts) {
  try {
    const parsed = new URL(url, 'https://example.com');
    if (allowedHosts.includes(parsed.hostname)) {
      return url;
    }
  } catch {
    // 無效 URL
  }
  return '/'; // 降級到首頁
}

3. 防止 XSS via URL

// ❌ 危險:javascript: 協定
const url = 'javascript:alert(document.cookie)';

// ✅ 安全:只允許 http/https 協定
function sanitizeUrl(url) {
  try {
    const parsed = new URL(url);
    if (['http:', 'https:'].includes(parsed.protocol)) {
      return url;
    }
  } catch {
    // 無效 URL
  }
  return 'about:blank';
}

URLPattern API(新標準)

// 路由匹配
const pattern = new URLPattern({ pathname: '/api/users/:id' });

const result = pattern.exec('https://example.com/api/users/123');
if (result) {
  console.log(result.pathname.groups.id); // "123"
}

// 更複雜的模式
const apiPattern = new URLPattern({
  protocol: 'https',
  hostname: ':env.example.com',
  pathname: '/api/:version/:resource/:id?',
});

const match = apiPattern.exec('https://prod.example.com/api/v2/users/456');
match.hostname.groups.env      // "prod"
match.pathname.groups.version  // "v2"
match.pathname.groups.resource // "users"
match.pathname.groups.id       // "456"

查詢參數構建最佳實踐

使用 URLSearchParams

// ✅ 推薦:使用 URLSearchParams
const params = new URLSearchParams({
  q: '搜尋關鍵字',
  sort: 'desc',
  page: '1',
  limit: '20',
});

params.append('tags', 'javascript');
params.append('tags', 'css');

const url = `https://api.example.com/search?${params}`;
// "https://api.example.com/search?q=...&sort=desc&page=1&limit=20&tags=javascript&tags=css"

陣列參數的常見慣例

慣例 範例 伺服器框架
重複鍵 ?tags=js&tags=css Express, Koa
括號後綴 ?tags[]=js&tags[]=css PHP, Rails
下標 ?tags[0]=js&tags[1]=css PHP
逗號分隔 ?tags=js,css 自訂

常見問題

為什麼 URL 中的空格有時是 %20 有時是 +?

  • URL 路徑中:空格 = %20(標準)
  • 查詢參數中:空格 = + 或 %20(+ 是 application/x-www-form-urlencoded 慣例)
  • 推薦:統一用 %20,避免歧義

URL 有最大長度限制嗎?

場景 限制
Chrome URL 欄 2MB
Firefox URL 欄 無限制
IE URL 欄 2083 字元
HTTP GET 請求 伺服器決定(通常 8KB)
安全上限 2048 字元(相容所有瀏覽器)

超過 2048 字元的參數應改用 POST 請求。


總結

URL 是 Web 的基石,理解其結構、編碼規則和安全處理是每個開發者的基本功。核心要點:參數值用 encodeURIComponent 編碼,驗證協定防止 XSS,白名單驗證防止開放重新導向。工具庫的 URL 編解碼工具 幫你快速處理 URL 編碼問題。

#URL解析#URL编码#查询参数#Web开发#教程