この記事は現在の言語では未提供です。zh-CN 版を表示しています。
URL 解析与编码完全指南:从结构拆解到安全处理的全流程实战
エンコード(更新: 2026年6月2日)
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
- 打开 URL 编解码工具
- 在输入框粘贴 URL 或文本
- 点击"编码"或"解码"
- 查看结果
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开发#教程