目标
URL标准通过以下方法使URL完全互操作:
-
使RFC 3986和RFC 3987与当代实现保持一致,并在此过程中使这些RFC过时。(例如,空格、其他“非法”代码点、查询编码、等同性、规范化,都是尚未完全共享或定义的概念。)URL解析需要像HTML解析一样可靠。[RFC3986] [RFC3987]
-
统一使用术语URL。URI和IRI容易混淆。实际上,两者都使用同一个算法,因此保持它们的区分对任何人都没有帮助。URL也轻松赢得了搜索结果的受欢迎度竞争。
-
详细定义URL现有的JavaScript API,并添加增强功能以使其更易于使用。为URL操作添加一个新的
URL
对象,无需使用HTML元素。(这对JavaScript worker环境很有用。) -
确保解析器、序列化器和API的组合保证幂等性。例如,解析-然后-序列化操作的非失败结果在应用进一步的解析-然后-序列化操作时不会改变。同样,通过API操作非失败结果时,应用任何数量的序列化-然后-解析操作也不会改变结果。
随着编辑们对该主题的深入了解,目标的范围可能会有所扩大。
1. 基础设施
本规范依赖于Infra。[INFRA]
本规范中使用的一些术语在以下标准和规范中定义:
- 编码 [ENCODING]
- 文件 API [FILEAPI]
- HTML [HTML]
- Unicode IDNA 兼容性处理 [UTS46]
- Web IDL [WEBIDL]
要序列化一个整数,将其表示为最短的十进制数。
1.1. 写入
验证错误 表示输入与有效输入不匹配。鼓励用户代理,尤其是符合性检查器,在某处报告这些错误。
验证错误并不意味着解析器终止。解析器的终止总是明确说明,例如,通过返回语句。
标记验证错误是有用的,因为错误处理可能不直观,遗留的用户代理可能未正确实现错误处理,所写内容的意图对其他开发者可能不清楚。
错误类型 | 错误描述 | 失败 |
---|---|---|
IDNA | ||
域名到ASCII |
Unicode ToASCII 记录错误或返回空字符串。 [UTS46] 如果记录了关于Unicode ToASCII的错误,鼓励用户代理将这些信息传递出去。 | 是 |
域名到Unicode |
Unicode ToUnicode 记录错误。[UTS46] 与域名到ASCII相同的注意事项适用。 | · |
主机解析 | ||
域名无效代码点 | 是 | |
主机无效代码点 | 是 | |
IPv4空部分 |
IPv4地址以U+002E (.)结尾。 | · |
IPv4部分太多 |
IPv4地址不包含正好4个部分。 | 是 |
IPv4非数字部分 |
IPv4地址的部分不是数字。 | 是 |
IPv4非十进制部分 |
IPv4地址包含用十六进制或八进制表示的数字。 | · |
IPv4部分超出范围 |
IPv4地址的部分超过255。 | 是 (仅适用于最后一部分) |
IPv6未关闭 |
IPv6地址缺少关闭的U+005D (])。 | 是 |
IPv6无效压缩 |
IPv6地址以不正确的压缩开头。 | 是 |
IPv6部分太多 |
IPv6地址包含超过8个部分。 | 是 |
IPv6多重压缩 |
IPv6地址在多个位置被压缩。 | 是 |
IPv6无效代码点 |
IPv6地址包含一个既不是ASCII十六进制数字也不是U+003A (:)的代码点。或者意外结束。 | 是 |
IPv6部分太少 |
未压缩的IPv6地址包含少于8个部分。 | 是 |
IPv4在IPv6中的部分太多 | 是 | |
IPv4在IPv6中的无效代码点 |
| 是 |
IPv4在IPv6中的部分超出范围 | 是 | |
IPv4在IPv6中的部分太少 | 是 | |
URL解析 | ||
无效URL单元 |
发现的代码点不是URL单元。 | · |
特殊方案缺少后续的斜线 |
输入的方案后面没有" | · |
缺少方案的非相对URL |
输入缺少方案,因为它不是以ASCII字母开头,并且未提供基础URL,或基础URL不能作为基础URL使用,因为它有一个不透明路径。 | 是 |
无效反斜线 |
URL有一个特殊方案,并且它使用U+005C (\)而不是U+002F (/)。 | · |
无效凭证 |
输入包含凭证。 | · |
主机缺失 | 是 | |
端口超出范围 |
输入的端口太大。 | 是 |
无效端口 |
输入的端口无效。 | 是 |
文件无效的Windows驱动器号 |
输入是相对URL字符串,其以Windows驱动器号开头,并且基础URL的方案是"
| · |
文件无效的Windows驱动器号主机 |
| · |
1.2. 解析器
EOF 代码点是一个概念性的代码点,表示字符串或代码点流的结束。
对于一个字符串输入,指针是一个整数,指向输入中的代码点。最初它指向输入的开始。如果它为-1,则表示没有指向。如果它大于或等于输入的代码点长度,则指向EOF代码点。
当使用指针时,c引用指针指向的代码点,只要它没有指向空处。当指针指向空处时,c无法使用。
当使用指针时,remaining引用从指针+1到字符串末尾的代码点子串,只要c不是EOF代码点。当c是EOF代码点时,remaining无法使用。
如果"mailto:username@example
"是正在处理的字符串,并且指针指向@,c是U+0040 (@),而remaining是"example
"。
如果正在处理的是空字符串,并且指针指向开始然后减1,使用c或remaining将会出错。
1.3. 百分比编码字节
百分比编码字节是U+0025 (%),后跟两个ASCII十六进制数字。
通常,对于百分比编码字节的序列,最好在百分比解码后再传递给UTF-8解码,无BOM或失败,以确保不会失败。其重要性取决于百分比编码字节的使用位置。例如,对于主机解析器,不遵循此建议是致命的,而对于URL渲染,这些百分比编码字节将不会百分比解码后呈现。
要百分比编码一个字节byte,返回由U+0025 (%),后跟表示byte的两个ASCII大写十六进制数字组成的字符串。
要百分比解码一个字节序列input,请按以下步骤操作:
当input包含非ASCII字节时,使用除UTF-8解码,无BOM外的任何内容都可能不安全,不推荐使用。
要百分比解码一个标量值字符串input:
通常,百分比编码会产生包含更多U+0025 (%)代码点的字符串,而百分比解码则会产生包含较少0x25 (%)字节的字节序列。
C0控制百分比编码集是C0控制和所有大于U+007E (~)的代码点。
片段百分比编码集是C0控制百分比编码集和U+0020空格、U+0022 (")、U+003C (<)、U+003E (>)以及U+0060 (`)。
查询百分比编码集是C0控制百分比编码集和U+0020空格、U+0022 (")、U+0023 (#)、U+003C (<)、U+003E (>)。
查询百分比编码集不能以片段百分比编码集定义,因为省略了U+0060 (`)。
特殊查询百分比编码集是查询百分比编码集和U+0027 (')。
路径百分比编码集是查询百分比编码集和U+003F (?)、U+0060 (`)、U+007B ({)、U+007D (})。
用户信息百分比编码集是路径百分比编码集和U+002F (/)、U+003A (:)、U+003B (;)、U+003D (=)、U+0040 (@)、U+005B ([)到U+005E (^)以及U+007C (|)。
组件百分比编码集是用户信息百分比编码集和U+0024 ($)到U+0026 (&),U+002B (+)以及U+002C (,)。
这由HTML用于registerProtocolHandler()
,也可被其他标准用于百分比编码数据,然后将其嵌入到URL的路径、查询或片段中,或在不透明主机中使用。与UTF-8百分比编码结合使用,结果与JavaScript的encodeURIComponent()
[sic]相同。[HTML][ECMA-262]
application/x-www-form-urlencoded
百分比编码集是组件百分比编码集和U+0021 (!)到U+0029 (右括号),以及U+007E (~)。
application/x-www-form-urlencoded
百分比编码集包含所有代码点,除了ASCII字母数字、U+002A (*)、U+002D (-)、U+002E (.)和U+005F (_)。
要进行编码后百分比编码,给定一个编码encoding,一个标量值字符串input,一个percentEncodeSet,以及一个可选的布尔值spaceAsPlus(默认值为false):
-
让encoder成为从encoding中获取编码器的结果。
-
将input转换为I/O队列,并称之为inputQueue。
-
让output为空字符串。
-
让potentialError为0。
这需要是一个非null的值,以启动后续的循环。
-
当potentialError不为null时:
-
返回output。
对于percentEncodeSet参数的可能值中,只有两个会对U+0025 (%)进行编码,从而产生“可往返的数据”:组件百分比编码集和application/x-www-form-urlencoded
百分比编码集。其他用于percentEncodeSet参数的值(如用于URL解析器的)将保持U+0025 (%)不变,因此首先需要对其进行UTF-8百分比编码以正确表示。
要UTF-8百分比编码一个标量值scalarValue,使用一个percentEncodeSet,返回运行编码后百分比编码的结果,使用UTF-8、scalarValue作为字符串,以及percentEncodeSet。
以下是上述操作的总结示例:
操作 | 输入 | 输出 |
---|---|---|
百分比编码input | 0x23 | "%23 "
|
0x7F | "%7F "
| |
百分比解码input | `%25%s%1G `
| `%%s%1G `
|
百分比解码input | "‽%25%2E "
| 0xE2 0x80 0xBD 0x25 0x2E |
编码后百分比编码,使用Shift_JIS、input和用户信息百分比编码集 | " "
| "%20 "
|
"≡ "
| "%81%DF "
| |
"‽ "
| "%26%238253%3B "
| |
编码后百分比编码,使用ISO-2022-JP、input和用户信息百分比编码集 | "¥ "
| "%1B(J\%1B(B "
|
编码后百分比编码,使用Shift_JIS、input、用户信息百分比编码集,并设置为true | "1+1 ≡ 2%20‽ "
| "1+1+%81%DF+2%20%26%238253%3B "
|
UTF-8百分比编码input,使用用户信息百分比编码集 | U+2261 (≡) | "%E2%89%A1 "
|
U+203D (‽) | "%E2%80%BD "
| |
UTF-8百分比编码input,使用用户信息百分比编码集 | "Say what‽ "
| "Say%20what%E2%80%BD "
|
2. 安全注意事项
URL的安全性取决于其环境。在呈现、解释和传递URL时需格外小心。
在呈现和分配新的URL时,需要考虑“欺骗”。这种攻击方式可能会让一个主机或URL与另一个混淆。例如,考虑如何1/l/I、m/rn/rri、0/O和а/a看起来都非常相似。更糟糕的是,考虑U+202A LEFT-TO-RIGHT EMBEDDING以及类似的代码点是不可见的。[UTR36]
在将URL从A传递给B时,双方都需要仔细考虑发生的情况。A可能会泄露它不希望泄露的数据。B可能会接收到意料之外的输入,并采取对用户有害的行动。尤其是,B永远不应信任A,因为在某些情况下,来自A的URL可能来自不受信任的来源。
3. 主机 (域名和IP地址)
在高层次上,主机、有效主机字符串、主机解析器和主机序列化器的关系如下:
-
主机可以被视为内存中的表示。
根据提供给主机解析器的isOpaque参数,解析-序列化往返操作会产生以下结果:
输入 | 输出 (isOpaque = false) | 输出 (isOpaque = true) |
---|---|---|
EXAMPLE.COM |
example.com (域名) |
EXAMPLE.COM (不透明主机) |
example%2Ecom |
example%2Ecom (不透明主机) |
|
faß.example |
xn--fa-hia.example (域名) |
fa%C3%9F.example (不透明主机) |
0 |
0.0.0.0 (IPv4) |
0 (不透明主机) |
%30 |
%30 (不透明主机) |
|
0x |
0x (不透明主机) |
|
0xffffffff |
255.255.255.255 (IPv4) |
0xffffffff (不透明主机) |
[0:0::1] |
[::1] (IPv6) |
|
[0:0::1%5D] |
失败 | |
[0:0::%31] |
||
09 |
失败 | 09 (不透明主机) |
example.255 |
example.255 (不透明主机) |
|
example^example |
失败 |
3.1. 主机表示
主机是一个域名、IP 地址、不透明主机或空主机。通常,主机用作网络地址,但有时在URL中用作不需要网络地址的不透明标识符。
典型的 URL 其主机为 不透明主机 的例子为
git://github.com/whatwg/url.git
。
下文引用的 RFC 仅供参考,对 主机 的编写、解析和序列化没有影响,除非在以下章节中另有说明。
域名是一个非空的ASCII 字符串,用于标识网络中的领域。[RFC1034]
域标签是域名 domain 上通过 严格拆分 U+002E (.) 得到的结果。
example.com
和 example.com.
这两个域名并不相同,通常被视为不同的域名。
IPv4 地址是一个 32 位无符号整数,用于标识网络地址。[RFC791]
IPv6 地址是一个 128 位无符号整数,用于标识网络地址。在本标准中,表示为一个由八个 16 位无符号整数组成的列表,也称为IPv6 片段。[RFC4291]
对 <zone_id>
的支持是故意省略的。
不透明主机是一个非空的ASCII 字符串,可以用于进一步处理。
空主机是一个空字符串。
3.2. 主机杂项
禁止的主机代码点是 U+0000 NULL, U+0009 TAB, U+000A LF, U+000D CR, U+0020 SPACE, U+0023 (#), U+002F (/), U+003A (:), U+003C (<), U+003E (>), U+003F (?), U+0040 (@), U+005B ([), U+005C (\), U+005D (]), U+005E (^), 或 U+007C (|)。
禁止的域名代码点是禁止的主机代码点、C0 控制、U+0025 (%) 或 U+007F DELETE。
主机输入 | 公共后缀 | 可注册域名 |
---|---|---|
com
| com
| null |
example.com
| com
| example.com
|
www.example.com
| com
| example.com
|
sub.www.example.com
| com
| example.com
|
EXAMPLE.COM
| com
| example.com
|
example.com.
| com.
| example.com.
|
github.io
| github.io
| null |
whatwg.github.io
| github.io
| whatwg.github.io
|
إختبار
| xn--kgbechtv
| null |
example.إختبار
| xn--kgbechtv
| example.xn--kgbechtv
|
sub.example.إختبار
| xn--kgbechtv
| example.xn--kgbechtv
|
[2001:0db8:85a3:0000:0000:8a2e:0370:7334]
| null | null |
规范应优先考虑源概念进行安全决策。“公共后缀”和“可注册域名”的概念不能作为提供硬性安全边界的依据,因为公共后缀列表会因客户端而有所不同。忽略此建议的规范应仔细考虑是否应将 URL 的方案纳入任何决策中,即是否应使用 同一站点或无方案同一站点的概念。
3.3. IDNA
域名到 ASCII算法,在给定字符串domain和布尔值beStrict的情况下,执行以下步骤:
-
运行Unicode ToASCII,domain_name设为domain,UseSTD3ASCIIRules设为beStrict,CheckHyphens设为 false,CheckBidi设为 true,CheckJoiners设为 true,Transitional_Processing设为 false,VerifyDnsLength设为beStrict,并将结果赋值给result。[UTS46]
如果beStrict为 false,domain是ASCII 字符串,并且严格分割 domain上的 U+002E (.) 不会产生任何项,且该项不会以ASCII 不区分大小写的"
xn--
"开头,那么该步骤等价于对domain进行ASCII 小写化。 -
返回result。
本文档和整个 Web 平台使用的是Unicode IDNA 兼容性处理,而不是
IDNA2008。例如,☕.example
变成xn--53h.example
,而不是失败。[UTS46][RFC5890]
域名到 Unicode算法,在给定域名domain和布尔值beStrict的情况下,执行以下步骤:
-
运行Unicode ToUnicode,domain_name设为domain,CheckHyphens设为 false,CheckBidi设为 true,CheckJoiners设为 true,UseSTD3ASCIIRules设为beStrict,Transitional_Processing设为 false,并将结果赋值给result。[UTS46]
-
标记域名到 Unicode的验证错误,然后返回result。
3.4. 主机写入
有效主机字符串必须是有效域名字符串,有效的 IPv4 地址字符串,或:U+005B ([),后跟一个有效的 IPv6 地址字符串,再加上 U+005D (])。
如果这些步骤返回成功,则domain为有效域名:
-
运行域名到 ASCII算法,使用domain和 true。
-
如果result为失败,则返回失败。
-
将result设置为运行域名到 Unicode算法的结果,使用result和 true。
-
如果result包含任何错误,则返回失败。
-
返回成功。
理想情况下,我们应该根据组成有效域名的代码点序列定义此内容,而不是使用逐个消除的方法:issue 245。
有效域名字符串必须是一个有效域名的字符串。
有效 IPv4 地址字符串必须是四个最短的可能由ASCII 数字组成的字符串,表示范围为 0 到 255(含)的十进制数,并由 U+002E (.) 分隔。
有效 IPv6 地址字符串在IP 版本 6 地址架构的"地址的文本表示"章节中定义。[RFC4291]
有效的不透明主机字符串必须是以下之一:
-
U+005B ([),后跟一个有效的 IPv6 地址字符串,再加上 U+005D (])。
这不是有效主机字符串定义的一部分,因为它需要在特定上下文中进行区分。
3.5. 主机解析
主机解析器接受一个标量值字符串 input,并带有可选的布尔值isOpaque(默认为 false),然后执行以下步骤。它们返回失败或主机。
-
如果input以 U+005B ([) 开始,那么:
-
如果isOpaque为 true,则返回运行不透明主机解析 input的结果。
-
断言:input不是空字符串。
-
运行无 BOM 的 UTF-8 解码,对input进行百分号解码的结果,得到domain。
或者,可以使用无 BOM 的 UTF-8 解码或失败,加上早期返回失败的操作,因为域名到 ASCII在遇到 U+FFFD (�) 时会失败。
-
运行域名到 ASCII算法,使用domain和 false,得到asciiDomain。
-
如果asciiDomain为失败,则返回失败。
-
返回asciiDomain。
结尾为数字检查器接受一个ASCII 字符串input,然后运行以下步骤。它们返回一个布尔值。
IPv4 解析器接受一个ASCII 字符串input,然后运行以下步骤。它们返回失败或一个IPv4 地址。
不要直接调用IPv4 解析器。而是检查主机解析器的返回值是否为IPv4 地址。
-
让parts是input通过 U+002E (.) 严格分割的结果。
-
如果parts的最后一项是空字符串,则:
-
让numbers成为一个空的列表。
-
对每个part的parts:
-
如果numbers中的任何项大于255,IPv4 超出范围项 验证错误。
-
如果numbers中除了最后一项之外的任何项大于255,则返回失败。
-
如果numbers中的最后一项大于等于 256(5 − numbers的大小),则返回失败。
-
让ipv4成为numbers中的最后一项。
-
移除numbers中的最后一项。
-
让counter为0。
-
对每个numbers的n:
-
通过n × 256(3 − counter)增加ipv4。
-
将counter加1。
-
-
返回ipv4。
IPv4 数字解析器接受一个ASCII 字符串input,然后运行以下步骤。它们返回失败或一个元组,包含一个数字和一个布尔值。
-
如果input是空字符串,则返回失败。
-
让validationError为 false。
-
让R为10。
-
如果input包含至少两个代码点,并且前两个代码点是"
0X
"或"0x
",则:-
将validationError设置为 true。
-
从input中移除前两个代码点。
-
将R设置为16。
-
-
否则,如果input包含至少两个代码点,并且第一个代码点是 U+0030 (0),则:
-
将validationError设置为 true。
-
从input中移除第一个代码点。
-
将R设置为8。
-
-
如果input是空字符串,则返回(0, true)。
-
如果input包含不是基数R的数字的代码点,则返回失败。
-
让output为input表示的基数R记数法中的整数值,使用ASCII 十六进制数字表示0到15之间的数字。
-
返回(output, validationError)。
IPv6 解析器接受一个标量值字符串input,然后运行以下步骤。它们返回失败或一个IPv6 地址。
IPv6 解析器理论上可以直接调用,但请先与本文件的编辑讨论。
-
让pieceIndex为0。
-
让compress为null。
-
让pointer成为input的指针。
-
如果c是 U+003A (:),则:
-
-
如果c是 U+003A (:),则:
-
让value和length为0。
-
当length小于4并且c是ASCII 十六进制数字时,将value设置为value × 0x10 + c作为十六进制数解释,并将pointer和length分别增加1。
-
如果c是 U+002E (.),则:
-
如果length为0,IPv4 在 IPv6 中无效代码点 验证错误,返回失败。
-
将pointer减少length。
-
如果pieceIndex大于6,IPv4 在 IPv6 中片段过多 验证错误,返回失败。
-
让numbersSeen为0。
-
-
让ipv4Piece为null。
-
如果numbersSeen大于0,则:
-
如果c是 U+002E (.) 并且numbersSeen小于4,则将pointer增加1。
- 否则,IPv4 在 IPv6 中无效代码点 验证错误,返回失败。
-
-
如果c不是ASCII 数字,IPv4 在 IPv6 中无效代码点 验证错误,返回失败。
-
-
让number成为c作为十进制数解释。
-
如果ipv4Piece为 null,则将ipv4Piece设置为number。
否则,如果ipv4Piece为0,IPv4 在 IPv6 中无效代码点 验证错误,返回失败。
否则,将ipv4Piece设置为ipv4Piece × 10 + number。
-
如果ipv4Piece大于255,IPv4 在 IPv6 中超出范围的片段 验证错误,返回失败。
-
将pointer增加1。
-
-
将address[pieceIndex]设置为address[pieceIndex] × 0x100 + ipv4Piece。
-
将numbersSeen增加1。
-
如果numbersSeen为2或4,则将pieceIndex增加1。
-
-
如果numbersSeen不是4,IPv4 在 IPv6 中片段过少 验证错误,返回失败。
-
跳出。
-
-
否则,如果c是 U+003A (:):
-
将pointer增加1。
-
如果c是EOF 代码点,IPv6 无效代码点 验证错误,返回失败。
-
-
否则,如果c不是EOF 代码点,IPv6 无效代码点 验证错误,返回失败。
-
将address[pieceIndex]设置为value。
-
将pieceIndex增加1。
-
如果compress非空,则:
-
让swaps为pieceIndex − compress。
-
将pieceIndex设置为7。
-
当pieceIndex不为0且swaps大于0时,交换address[pieceIndex]与address[compress + swaps − 1],然后将pieceIndex和swaps均减少1。
-
-
返回address。
不透明主机解析器接受一个标量值字符串input,然后运行以下步骤。它们返回失败或一个不透明主机。
-
如果input包含一个U+0025 (%) 且其后的两个代码点不是ASCII十六进制数字,无效URL单位验证错误。
-
返回使用C0控制百分比编码集对input运行UTF-8百分比编码的结果。
3.6. 主机序列化
IPv6序列化器接受一个IPv6地址address,然后运行以下步骤。它们返回一个ASCII字符串。
-
让output为空字符串。
-
让compress为指向address中第一段0最长序列的第一个IPv6片段的索引。
-
如果address中没有比长度为1更长的0序列,则将compress设置为null。
-
让ignore0为false。
-
对于每个在范围0到7(含)的pieceIndex:
-
返回output。
该算法需要遵循[RFC5952]中的建议。
3.7. 主机等效性
证书比较需要一个忽略域名末尾点(如果有的话)的主机等效性检查。然而,这些主机还强制执行了诸如DNS长度等其他方面的验证,而这些在此处未强制执行,因为URL不强制执行它们。如果有人对如何将这两者更紧密结合,或什么是好的统一模型有好的建议,请提交问题。
4. URL
在高层次上,URL,有效的URL字符串,URL解析器和URL序列化器的关系如下:
输入 | 基址 | 有效 | 输出 |
---|---|---|---|
https:example.org
| ❌ | https://example.org/
| |
https://////example.com///
| ❌ | https://example.com///
| |
https://example.com/././foo
| ✅ | https://example.com/foo
| |
hello:world
| https://example.com/
| ✅ | hello:world
|
https:example.org
| https://example.com/
| ❌ | https://example.com/example.org
|
\example\..\demo/.\
| https://example.com/
| ❌ | https://example.com/demo/
|
example
| https://example.com/demo
| ✅ | https://example.com/example
|
file:///C|/demo
| ❌ | file:///C:/demo
| |
..
| file:///C:/demo
| ✅ | file:///C:/
|
file://loc%61lhost/
| ✅ | file:///
| |
https://user:password@example.org/
| ❌ | https://user:password@example.org/
| |
https://example.org/foo bar
| ❌ | https://example.org/foo%20bar
| |
https://EXAMPLE.com/../x
| ✅ | https://example.com/x
| |
https://ex ample.org/
| ❌ | 失败 | |
example
| ❌,因为缺少基址 | 失败 | |
https://example.com:demo
| ❌ | 失败 | |
http://[www.example.com]/
| ❌ | 失败 | |
https://example.org//
| ✅ | https://example.org//
| |
https://example.com/[]?[]#[]
| ❌ | https://example.com/[]?[]#[]
| |
https://example/%?%#%
| ❌ | https://example/%?%#%
| |
https://example/%25?%25#%25
| ✅ | https://example/%25?%25#%25
|
4.1. URL 表示
URL 是一个结构体,表示一个通用标识符。为了与有效的URL字符串区分开来,它也可以被称为URL记录。
URL的方案是一个ASCII字符串,用于标识URL的类型,并可以用于在解析后进一步处理URL。它最初为空字符串。
URL的用户名是一个ASCII字符串,用于标识用户名。它最初为空字符串。
URL的密码是一个ASCII字符串,用于标识密码。它最初为空字符串。
URL的端口为null或标识网络端口的16位无符号整数。最初为null。
URL的路径是一个URL路径,通常用于标识位置。最初为« »。
URL的查询为null或一个ASCII字符串。最初为null。
URL的片段为null或ASCII字符串,可用于对URL的其他组件标识的资源进行进一步处理。最初为null。
URL还关联有一个Blob URL条目,它可以是null或Blob URL条目。最初为null。
这是用于支持缓存“blob
”URL引用的对象及其来源的。这一点很重要,因为URL可能会在解析和抓取之间从Blob
URL存储中移除,但抓取仍然需要成功。
下表列出了当有效的URL字符串被解析时,它们如何映射到URL的组件。用户名,密码和Blob URL条目被省略;在下例中,它们分别为空字符串、空字符串和null。
输入 | 方案 | 主机 | 端口 | 路径 | 查询 | 片段 |
---|---|---|---|---|---|---|
https://example.com/
| "https "
| "example.com "
| null | « 空字符串 » | null | null |
https://localhost:8000/search?q=text#hello
| "https "
| "localhost "
| 8000 | « "search " »
| "q=text "
| "hello "
|
urn:isbn:9780307476463
| "urn "
| null | null | "isbn:9780307476463 "
| null | null |
file:///ada/Analytical%20Engine/README.md
| "file "
| null | null | « "ada ", "Analytical%20Engine ", "README.md " »
| null | null |
URL路径是一个URL路径段或一个包含零个或多个URL路径段的列表。
URL路径段是一个ASCII字符串。它通常指的是目录或文件,但没有预定义的含义。
单点URL路径段是一个URL路径段,即为".
"或ASCII不区分大小写匹配的"%2e
"。
双点URL路径段是一个URL路径段,即为"..
"或ASCII不区分大小写匹配的".%2e
"、"%2e.
"或"%2e%2e
"。
4.2. URL 杂项
特殊方案是一个ASCII字符串,列在下表的第一列中。特殊方案的默认端口列在同一行的第二列。任何其他ASCII字符串的默认端口为null。
特殊方案 | 默认端口 |
---|---|
"ftp "
| 21 |
"file "
| null |
"http "
| 80 |
"https "
| 443 |
"ws "
| 80 |
"wss "
| 443 |
URL 是特殊的,如果它的方案是特殊方案。URL不是特殊的,如果它的方案不是特殊方案。
URL如果其主机为null或为空字符串,或者其方案是"file
"
,则不能具有用户名/密码/端口。
URL可以被指定为基础URL。
基础URL对于URL解析器很有用,当输入可能是相对URL字符串时。
Windows 驱动器号 是两个码点,第一个是 ASCII 字母,第二个是 U+003A (:) 或 U+007C (|)。
规范化的 Windows 驱动器号 是 Windows 驱动器号,其中第二个码点是 U+003A (:)。
根据 URL 写入 部分,只有 规范化的 Windows 驱动器号 是合规的。
如果符合以下所有条件,则字符串 以 Windows 驱动器号开头:
- 它的长度大于或等于2
- 它的前两个码点是Windows 驱动器号
- 它的长度为2,或其第三个码点是 U+002F (/)、U+005C (\)、U+003F (?) 或 U+0023 (#)。
要缩短 url 的路径:
4.3. URL 写入
有效 URL 字符串 必须是 相对 URL 带片段字符串 或 绝对 URL 带片段字符串之一。
绝对 URL 带片段字符串 必须是 绝对 URL 字符串,可选地后跟 U+0023 (#) 和 URL 片段字符串。
绝对 URL 字符串 必须是以下之一:
-
一个 URL 方案字符串,它是 ASCII 不区分大小写的 特殊方案匹配,但不是 "
file
" 的 ASCII 不区分大小写匹配,后跟 U+003A (:) 和 方案相对特殊 URL 字符串 -
一个 URL 方案字符串,它不是 ASCII 不区分大小写的 特殊方案匹配,后跟 U+003A (:) 和 相对 URL 字符串
-
一个 URL 方案字符串,它是 "
file
" 的 ASCII 不区分大小写匹配,后跟 U+003A (:) 和 方案相对文件 URL 字符串
任何可选地后跟 U+003F (?) 和 URL 查询字符串。
URL 方案字符串 必须是一个 ASCII 字母,后跟零个或多个 ASCII 字母数字字符、U+002B (+)、U+002D (-) 和 U+002E (.)。
相对 URL 带片段字符串 必须是 相对 URL 字符串,可选地后跟 U+0023 (#) 和 URL 片段字符串。
相对 URL 字符串 必须是以下之一,基于 基本 URL 的 方案进行切换:
- 一个 特殊方案,不是
"
file
" -
一个 路径绝对 URL 字符串
- "
file
" -
如果 基本 URL 的 主机是空主机,则一个 路径绝对 URL 字符串
如果 基本 URL 的 主机不是空主机,则一个 非 Windows 文件路径绝对 URL 字符串
- 否则
-
一个 方案相对 URL 字符串
一个 路径绝对 URL 字符串
可选地后跟 U+003F (?) 和 URL 查询字符串。
方案相对特殊 URL
字符串 必须是
"//
",后跟一个 有效主机字符串,可选地后跟 U+003A (:) 和 URL 端口字符串,再可选地后跟 绝对路径 URL 字符串。
URL 端口字符串 必须是以下之一:
-
空字符串
-
一个或多个 ASCII 数字,表示不超过 216 - 1 的十进制数字。
方案相对 URL 字符串 必须是 "//
",后跟 不透明主机和端口字符串,可选地后跟 绝对路径 URL
字符串。
不透明主机和端口字符串 必须是空字符串或: 有效的不透明主机字符串,可选地后跟 U+003A (:) 和 URL 端口字符串。
方案相对文件 URL
字符串 必须是 "//
",后跟以下之一:
-
一个 有效主机字符串,可选地后跟 非 Windows 文件路径绝对 URL 字符串
-
一个 路径绝对 URL 字符串。
路径绝对 URL 字符串 必须是 U+002F (/),后跟一个 路径相对 URL 字符串。
非 Windows 文件路径绝对 URL 字符串 必须是一个 路径绝对 URL 字符串,但不以 U+002F (/) 开头,后跟 Windows 驱动器字母,再跟 U+002F (/)。
路径相对 URL 字符串 必须是零个或多个 URL 路径段字符串,以 U+002F (/) 分隔,且不以 U+002F (/) 开头。
路径相对无方案 URL 字符串 必须是一个 路径相对 URL 字符串,但不以 URL 方案字符串 开头,后跟 U+003A (:)。
URL 路径段字符串 必须是以下之一:
URL 查询字符串 必须是零个或多个 URL 单位。
URL 片段字符串 必须是零个或多个 URL 单位。
URL 代码点 是 ASCII 字母数字,U+0021 (!),U+0024 ($),U+0026 (&),U+0027 ('),U+0028 (左括号),U+0029 (右括号),U+002A (*),U+002B (+),U+002C (,),U+002D (-),U+002E (.),U+002F (/),U+003A (:),U+003B (;),U+003D (=),U+003F (?),U+0040 (@),U+005F (_),U+007E (~),以及 代码点 范围从 U+00A0 到 U+10FFFD,排除 代理项 和 非字符。
代码点大于 U+007F DELETE 时,将由 百分号编码字节 转换为 URL 解析器。
在 HTML 中,当文档编码为遗留编码时,URL 查询字符串 中高于 U+007F DELETE 的代码点将由文档的编码转换为 百分号编码字节。如果一个文档中的 URL 被复制到使用不同编码的另一个文档中,这可能会导致问题。使用 UTF-8 编码可以解决这个问题。
例如,考虑以下 HTML 文档:
<!doctype html>
< meta charset = "windows-1252" >
< a href = "?smörgåsbord" > Test</ a >
由于文档编码为 windows-1252,链接的 URL 的
查询字符串 将是
"sm%F6rg%E5sbord
"。如果文档编码是 UTF-8,它将是
"sm%C3%B6rg%C3%A5sbord
"。
百分号编码字节 可用于编码不属于 URL 代码点 或被排除写入的代码点。
无法在 有效的 URL 字符串 中表示 用户名 或 密码。
4.4. URL 解析
URL 解析器 接受一个标量值字符串 input,带有可选的 null 或 基本 URL base(默认为 null)和可选的 编码 encoding(默认为 UTF-8),并运行以下步骤:
非网页浏览器的实现只需实现基本 URL 解析器。
本标准不包括浏览器地址栏中用户输入转换为URL 记录的过程。标准确实包含了 URL 渲染要求,因为它们涉及信任决策。
-
让 url 为在 input 上使用 base 和 encoding 运行 基本 URL 解析器 的结果。
-
如果 url 为失败,返回失败。
-
如果 url 的 方案 不是 "
blob
",返回 url。 -
将 url 的 blob URL 条目 设置为 解析 blob URL url 的结果, 如果未返回失败,则为 null。
-
返回 url。
基本 URL 解析器 接受一个标量值字符串 input,带有可选的 null 或基本 URL base(默认为 null),可选的 编码 encoding (默认为 UTF-8), 可选的 URL url, 以及一个可选的状态覆盖 state override, 然后运行以下步骤:
参数 encoding 是一个仅与 HTML 相关的遗留概念。url 和 state override 参数仅用于某些 API。[HTML]
当没有传递 url 和 state override 参数时,基本 URL 解析器 返回一个新的 URL 或失败。如果传递了它们, 该算法将修改传递的 url,并且可能在没有返回结果的情况下终止。
-
如果没有给出 url:
-
将 url 设置为一个新的 URL。
-
如果 input 包含任何前导或尾随的C0 控制或空格,则触发 无效的 URL 单元 验证错误。
-
从 input 中移除任何前导和尾随的 C0 控制或空格。
-
-
如果 input 包含任何ASCII 制表符或换行符,则触发 无效的 URL 单元 验证错误。
-
从 input 中移除所有 ASCII 制表符或换行符。
-
将 state 设置为 state override(如果给定),否则为 scheme start state。
-
将 encoding 设置为从 encoding 中获取输出编码的结果。
-
将 buffer 设为空字符串。
-
将 atSignSeen、insideBrackets 和 passwordTokenSeen 设置为 false。
-
为 input 创建一个 指针 pointer。
-
通过切换 state 来持续运行以下状态机。如果在运行之后 pointer 指向EOF 码点,进入下一步。否则,增加 pointer 的值 1,并继续状态机。
- 方案开始状态
- 方案状态
-
-
如果 c 是 ASCII 字母或数字,U+002B (+)、U+002D (-) 或 U+002E (.),将小写后的 c 追加到 buffer。
-
否则,如果 c 是 U+003A (:),那么:
-
否则,如果没有给定 state override,将 buffer 设为空字符串,state 设为 无方案状态,并重新开始(从 input 中的第一个字符开始)。
-
否则,返回失败。
此失败仅供
Location
对象的protocol
setter 使用。此外,方案状态中定义的非失败退出是专门为了定义 setter。
-
- 无方案状态
-
-
如果 base 为空,或者 base 有一个 不透明路径,并且 c 不是 U+0023 (#),触发 缺少方案的非相对URL 验证错误,返回失败。
-
否则,如果 base 有一个 不透明路径,并且 c 是 U+0023 (#),将 url 的 方案 设置为 base 的 方案,将 url 的 路径 设置为 base 的 路径,将 url 的 查询 设置为 base 的 查询,将 url 的 片段 设置为空字符串,并 将 state 设置为 片段状态。
-
否则,如果 base 的 方案 不是 "
file
",将 state 设置为 相对状态, 并将 pointer 减少 1。 -
否则,将 state 设置为 文件状态, 并将 pointer 减少 1。
-
- 特殊相对或权限状态
-
-
如果 c 是 U+002F (/) 并且 剩余 以 U+002F (/) 开头,将 state 设置为 特殊权限忽略斜杠状态,并将 pointer 增加 1。
-
否则,触发 特殊方案缺少后续斜杠 验证错误,将 state 设置为 相对状态,并将 pointer 减少 1。
-
- 路径或权限状态
- 相对状态
- 相对斜杠状态
- 特殊权限斜杠状态
-
-
如果 c 是 U+002F (/) 并且 剩余部分 以 U+002F (/) 开头,将 state 设置为 特殊权限忽略斜杠状态,并将 pointer 增加 1。
-
否则,触发 特殊方案缺少后续斜杠 验证错误,将 state 设置为 特殊权限忽略斜杠状态,并减少 pointer 1。
-
- 特殊权限忽略斜杠状态
-
-
如果 c 既不是 U+002F (/) 也不是 U+005C (\),则将 state 设置为 权限状态,并减少 pointer 1。
-
否则,触发 特殊方案缺少后续斜杠 验证错误。
-
- 权限状态
-
-
如果 c 是 U+0040 (@),则:
-
如果 atSignSeen 为 true,则在 buffer 前面添加 "
%40
"。 -
将 atSignSeen 设置为 true。
-
对于 buffer 中的每个 codePoint:
-
如果 codePoint 是 U+003A (:) 且 passwordTokenSeen 为 false, 则将 passwordTokenSeen 设置为 true,并 继续。
-
将 codePoint 运行 UTF-8 百分号编码,使用 用户信息百分号编码集。
-
如果 passwordTokenSeen 为 true,则将 encodedCodePoints 添加到 url 的 密码。
-
否则,将 encodedCodePoints 添加到 url 的 用户名。
-
-
将 buffer 设置为空字符串。
-
否则,如果以下之一为 true:
那么:
-
否则,将 c 添加到 buffer。
-
- 主机状态
- 主机名状态
- 端口状态
-
-
否则,如果以下任一条件为真:
那么:
- 文件状态
-
-
将 url 的 scheme 设置为 "
file
"。 -
将 url 的 主机 设置为空字符串。
-
如果 c 是 U+002F (/) 或 U+005C (\),则:
-
否则,如果 base 非空且 base 的 scheme 是 "
file
": -
否则,将 state 设置为 路径状态,并将 pointer 减少 1。
-
- 文件斜杠状态
-
-
如果 c 是 U+002F (/) 或 U+005C (\),则:
-
否则:
-
- 文件主机状态
-
-
如果 c 是 EOF 代码点、U+002F (/)、 U+005C (\)、U+003F (?) 或 U+0023 (#),则将 pointer 减少 1,然后:
-
如果没有给出 state override 且 buffer 是 Windows 驱动器字母,则 文件无效的 Windows 驱动器字母主机 验证错误, 将 state 设置为 路径状态。
这是一个(平台无关的)Windows 驱动器字母怪癖。buffer 在此处未重置,而是在 路径状态 中使用。
-
否则,如果 buffer 是空字符串,则:
-
否则,执行以下步骤:
-
-
否则,将 c 追加到 buffer。
-
- 路径开始状态
- 路径状态
-
-
如果以下情况之一为真:
-
没有给出 state override 且 c 是 U+003F (?) 或 U+0023 (#)
那么:
-
如果 buffer 是 双点 URL 路径段,那么:
-
否则,如果 buffer 是 单点 URL 路径段,且 c 既不是 U+002F (/),也不是 url 特殊的 且 c 是 U+005C (\),则 追加空字符串到 url 的 路径。
-
否则,如果 buffer 不是 单点 URL 路径段,那么:
-
将 buffer 设置为空字符串。
-
否则,执行以下步骤:
-
- 不透明路径状态
-
-
否则:
- 查询状态
-
-
如果 encoding 不是 UTF-8 且以下情况之一为真:
则将 encoding 设置为 UTF-8。
-
如果以下情况之一为真:
那么:
-
设 queryPercentEncodeSet 为 特殊查询百分比编码集 如果 url 是特殊的;否则为 查询百分比编码集。
-
百分比编码后编码,使用 encoding、 buffer 和 queryPercentEncodeSet,并将结果追加到 url 的 查询。
由于有状态的 ISO-2022-JP 编码器,该操作不能逐代码点调用。
-
将 buffer 设置为空字符串。
-
-
- 片段状态
-
返回 url。
要给定 url 和 username 设置用户名,将 url 的 用户名 设置为运行 UTF-8 百分比编码 在 username 上,并使用 用户信息百分比编码集 的结果。
要给定 url 和 password 设置密码,将 url 的 密码 设置为运行 UTF-8 百分比编码 在 password 上,并使用 用户信息百分比编码集 的结果。
4.5. URL 序列化
URL 序列化器 接收一个 URL url,以及一个可选的布尔值 exclude fragment(默认为 false),然后执行以下步骤。它们返回一个 ASCII 字符串。
-
让 output 为 url 的 scheme 和 U+003A (:) 连接后的结果。
-
如果 url 的 主机 非空:
-
如果 url 的 主机 为空,url 没有 不透明路径,url 的 路径的 大小 大于 1,且 url 的 路径[0] 是空字符串,则添加 U+002F (/) 和 U+002E (.) 到 output。
这可以防止
web+demo:/.//not-a-host/
或web+demo:/path/..//not-a-host/
在 解析然后 序列化后,最终变成web+demo://not-a-host/
(它们最终会变成web+demo:/.//not-a-host/
)。 -
将 URL 路径序列化 url 的结果添加到 output。
-
如果 exclude fragment 为 false 且 url 的 片段 非空,则添加 U+0023 (#),后接 url 的 片段,到 output。
-
返回 output。
4.6. URL 等价性
4.7. 源
请参阅 源在 HTML 中的定义,获取必要的背景信息。[HTML]
URL 的源是通过以下步骤,根据 url 的 方案 切换得出的:
- "
blob
" -
-
如果 url 的 blob URL 项 非空,则返回 url 的 blob URL 项 的 环境 的 源。
-
如果 pathURL 失败,则返回一个新的不透明源。
-
返回一个新的不透明源。
blob:https://whatwg.org/d0360e2f-caee-469f-9a2f-87d5b0456f6f
的源是元组源 ("https
"、"whatwg.org
"、null、null)。 -
- "
ftp
"- "
http
"- "
https
"- "
ws
"- "
wss
" - "
- "
file
" -
尽管令人遗憾,这个问题留给读者自行解决。当不确定时,返回一个新的不透明源。
- 其他
-
返回一个新的不透明源。
4.8. URL 渲染
当显示 URL 的主要目的是让用户做出安全或信任决定时,应该以其 序列化形式进行渲染,并进行如下所述的修改。例如,用户应根据浏览器地址栏中渲染的 URL 来做出信任决定。
4.8.1. 简化非人类可读或无关的组件
删除可能提供欺骗机会或分散安全相关信息的组件:
-
在用户区分 URL 的主机和其他部分(如 路径)时,浏览器可以只显示 URL 的 主机。浏览器还可以进一步简化主机,突出其 可注册域。例如,浏览器可以省略前导的
www
或m
域标签,或只显示其可注册域,以消除子域名引发的欺骗机会(例如,https://examplecorp.attacker.com/
)。 -
浏览器不应渲染 URL 的 用户名和密码,因为它们可能会被误认为是 URL 的 主机(例如,
https://examplecorp.com@attacker.example/
)。 -
如果显示界面仅允许使用单一方案,浏览器可以不渲染 URL 的 方案(例如浏览器功能省略了
https://
,因为它仅对安全来源启用)。否则,方案可以被替换或补充为人类可读的字符串(例如“非安全”),安全指示符图标,或两者兼具。
4.8.2. 省略
在空间受限的显示中,URL 应小心省略,以避免误导用户做出安全决定:
-
浏览器应确保在渲染 URL 时至少显示可注册域(以避免显示
...examplecorp.com
时实际加载的是https://not-really-examplecorp.com/
)。 -
当无法渲染完整的主机时,浏览器应从最低级别域标签开始省略域标签。例如,
examplecorp.com.evil.com
应被省略为...com.evil.com
,而不是examplecorp.com...
。(请注意,双向文本意味着最低级别的域标签可能不会出现在左侧。)
4.8.3. 国际化和特殊字符
应谨慎处理国际化域名(IDNs)、特殊字符和双向文本,以防止欺骗:
-
浏览器应通过运行 域名到 Unicode,使用 URL 的 主机并返回 false 来渲染其主机。
-
当 URL 包含双向文本时,特别容易导致主机和路径之间的混淆,因此建议仅渲染 URL 的主机。为提高可读性,如果渲染 URL 的其他部分,应该将 百分比编码字节序列替换为通过运行 无 BOM 的 UTF-8 解码后得到的代码点,除非这些序列会变得不可见。浏览器可以选择不解码某些呈现欺骗风险的序列(例如,U+1F512 (🔒))。
-
浏览器应将双向文本渲染为左到右的嵌入文本。[BIDI]
不幸的是,由于渲染的 URL 是字符串,可以出现在任何地方,因此专门用于渲染 URL 的双向算法不会得到广泛采用。双向文本与 URL 的部分交互,可能导致渲染结果与模型不同。双向语言的用户可能会习惯这一点,特别是在纯文本环境中。
5.
application/x-www-form-urlencoded
application/x-www-form-urlencoded
格式提供了一种对 列表 中的 元组(每个元组包含一个名称和一个值)进行编码的方法。
application/x-www-form-urlencoded
格式在许多方面是一种异常的怪物,它是多年实现事故和妥协的结果,导致了一组为了互操作性而必需的要求,但绝不代表良好的设计实践。尤其是,读者应特别注意涉及字符编码和字节序列之间的多次(有时是嵌套的)转换的复杂细节。不幸的是,由于
HTML 表单的广泛使用,这种格式被广泛使用。[HTML]
5.1. application/x-www-form-urlencoded
解析
旧的面向服务器的实现可能必须支持 编码,如 UTF-8
以外的编码,以及对名称为 _charset
的元组进行特殊处理。此处未描述此类逻辑,因为仅 UTF-8 是合规的。
application/x-www-form-urlencoded
解析器 接受一个字节序列
input,然后运行以下步骤:
-
将 sequences 设为通过 0x26 (&) 拆分 input 的结果。
-
将 output 设为一个最初为空的名称-值元组的 列表,其中名称和值都存储为字符串。
-
对每个 字节序列 bytes 在 sequences 中:
-
如果 bytes 是空字节序列,则 继续。
-
如果 bytes 包含 0x3D (=),则将 name 设为从 bytes 开头到其第一个 0x3D (=)(不包括)的字节,value 设为(如果有)从第一个 0x3D (=) 之后到 bytes 结尾的字节。如果 0x3D (=) 是第一个字节,则 name 将为空字节序列。如果是最后一个字节,则 value 将为空字节序列。
-
否则,将 name 设为 bytes 的值,并将 value 设为空字节序列。
-
将 name 和 value 中的任何 0x2B (+) 替换为 0x20 (SP)。
-
将 nameString 和 valueString 设为分别对 name 和 value 进行无 BOM 的 UTF-8 解码并 百分比解码 的结果。
-
将 (nameString, valueString) 附加到 output。
-
-
返回 output。
5.2. application/x-www-form-urlencoded
序列化
application/x-www-form-urlencoded
序列化器
接受一个名称-值元组的列表 tuples,可选地带有一个编码
encoding(默认 UTF-8),然后运行以下步骤。它们返回一个ASCII 字符串。
-
将 encoding 设置为通过 获取输出编码 获得的结果。
-
将 output 设为空字符串。
-
对每个 tuple 在 tuples 中:
-
将 name 设为运行 百分比编码后编码,使用 encoding、tuple 的名称、
application/x-www-form-urlencoded
百分比编码集,并且为 true。 -
将 value 设为运行 百分比编码后编码,使用 encoding、tuple 的值、
application/x-www-form-urlencoded
百分比编码集,并且为 true。 -
如果 output 不是空字符串,则附加 U+0026 (&) 到 output。
- 将 name,接着 U+003D (=),接着 value,附加到 output。
- 返回 output。
5.3. 挂钩
application/x-www-form-urlencoded
字符串解析器 接受一个标量值字符串 input,UTF-8 编码 它,然后返回 application/x-www-form-urlencoded
解析 它的结果。
6. API
本节使用了 Web IDL 中的术语。浏览器用户代理必须支持此 API。JavaScript 实现应支持此 API。其他用户代理或编程语言则被鼓励使用适合其需求的 API,可能不是此 API。[WEBIDL]
6.1. URL 类
[Exposed=*,LegacyWindowAlias =]
webkitURL interface {
URL constructor (USVString ,
url optional USVString );
base static URL ?parse (USVString ,
url optional USVString );
base static boolean canParse (USVString ,
url optional USVString );
base stringifier attribute USVString href ;readonly attribute USVString origin ;attribute USVString protocol ;attribute USVString username ;attribute USVString password ;attribute USVString host ;attribute USVString hostname ;attribute USVString port ;attribute USVString pathname ;attribute USVString search ; [SameObject ]readonly attribute URLSearchParams searchParams ;attribute USVString hash ;USVString toJSON (); };
A URL
对象有一个关联的:
- URL:一个 URL。
- 查询对象:一个
URLSearchParams
对象。
为了 可能从不透明路径中去除尾随空格,给定一个 URL
对象 url:
API URL 解析器 接受一个 标量值字符串 url 和一个可选的 null 或 标量值字符串 base(默认为 null),然后运行以下步骤:
-
让 parsedBase 为 null。
-
如果 base 不为 null:
-
将 parsedBase 设置为运行 基本 URL 解析器 在 base 上的结果。
-
如果 parsedBase 失败,则返回失败。
-
-
返回运行 基本 URL 解析器 在 url 和 parsedBase 上的结果。
new URL(url, base)
构造函数的步骤如下:
-
将 parsedURL 设置为运行 API URL 解析器 在 url 和 base 上的结果,如果有提供 base。
为了不使用 基础 URL 将字符串 解析为一个 URL,调用带一个参数的 URL
构造函数:
var input = "https://example.org/💩" ,
url = new URL( input)
url. pathname // "/%F0%9F%92%A9"
当输入是 相对 URL 字符串 时会抛出异常:
try {
var url = new URL( "/🍣🍺" )
} catch ( e) {
// 出现了异常
}
在这些情况下,必须使用一个 基础 URL:
var input = "/🍣🍺" ,
url = new URL( input, document. baseURI)
url. href // "https://url.spec.whatwg.org/%F0%9F%8D%A3%F0%9F%8D%BA"
一个 URL
对象可以用作 基础 URL(由于 IDL
需要字符串作为参数,一个 URL
对象字符串化为其 href
获取器的返回值):
var url = new URL( "🏳️🌈" , new URL( "https://pride.example/hello-world" ))
url. pathname // "/%F0%9F%8F%B3%EF%B8%8F%E2%80%8D%F0%9F%8C%88"
静态方法 parse(url, base)
的步骤如下:
-
将 parsedURL 设置为运行 API URL 解析器 在 url 和 base 上的结果,如果有提供 base。
-
如果 parsedURL 失败,则返回 null。
-
让 url 成为一个新的
URL
对象。 -
初始化 url,使用 parsedURL。
-
返回 url。
静态方法 canParse(url, base)
的步骤如下:
-
将 parsedURL 设置为运行 API URL 解析器 在 url 和 base 上的结果,如果有提供 base。
-
如果 parsedURL 失败,则返回 false。
-
返回 true。
步骤
href
设置器如下:
步骤
protocol
设置器 是使用 基本 URL 解析 进行设置,给定值后跟 U+003A (:),并将 this 的 URL 作为 url,以及 scheme start state 作为 状态覆盖。
步骤 host
获取器如下:
步骤
host
设置器如下:
如果设置器的给定值没有 端口,那么 this 的 URL 的 端口 将不会改变。这可能会让人感到意外,因为
host
获取器返回 URL-端口字符串,因此可能有人认为设置器会始终“重置”两者。
步骤
hostname
设置器如下:
步骤
port
设置器如下:
步骤
pathname
设置器如下:
步骤
search
设置器如下:
-
如果给定值是空字符串:
-
将给定值中的单个前导 U+003F (?) 移除(如果存在),然后让 input 成为结果。
-
将 url 的 查询 设置为空字符串。
-
使用 基本 URL 解析器 对 input 进行解析,传递 url 作为 url 和 查询状态 作为 状态覆盖。
该 search
设置器可能会从 this
的 URL 的 路径 中移除尾随 U+0020 空格
码位。这是为了确保对 URL 序列化器 输出运行 URL 解析器 后不会得到一个与 URL 不 相等 的结果。
步骤
hash
设置器如下:
-
如果给定值是空字符串:
-
使用 可能从不透明路径中去除尾随空格 来处理 this。
-
返回。
-
将给定值中的单个前导 U+0023 (#) 移除(如果存在),然后让 input 成为结果。
-
使用 基本 URL 解析器 对 input 进行解析,传递 this 的 URL 作为 url 和 片段状态 作为 状态覆盖。
6.2. URLSearchParams 类
[Exposed=*]interface {
URLSearchParams constructor (optional (sequence <sequence <USVString >>or record <USVString ,USVString >or USVString )= "");
init readonly attribute unsigned long size ;undefined append (USVString ,
name USVString );
value undefined delete (USVString ,
name optional USVString );
value USVString ?get (USVString );
name sequence <USVString >getAll (USVString );
name boolean has (USVString ,
name optional USVString );
value undefined set (USVString ,
name USVString );
value undefined sort ();iterable <USVString ,USVString >;stringifier ; };
构造并序列化一个 URLSearchParams
对象非常简单:
let params = new URLSearchParams({ key: "730d67" })
params. toString() // "key=730d67"
由于 URLSearchParams
对象使用的是 application/x-www-form-urlencoded
格式,因此它对某些代码点的编码与
URL
对象(包括 href
和 search
)有所不同。当使用
searchParams
操作一个 URL 的 query
时,这尤其容易让人感到意外。
const url = new URL( 'https://example.com/?a=b ~' );
console. log( url. href); // "https://example.com/?a=b%20~"
url. searchParams. sort();
console. log( url. href); // "https://example.com/?a=b+%7E"
const url = new URL( 'https://example.com/?a=~&b=%7E' );
console. log( url. search); // "?a=~&b=%7E"
console. log( url. searchParams. get( 'a' )); // "~"
console. log( url. searchParams. get( 'b' )); // "~"
URLSearchParams
对象会对 application/x-www-form-urlencoded
percent-encode set 中的任何字符进行百分比编码,并将 U+0020 SPACE 编码为 U+002B (+)。
忽略编码(使用 UTF-8),search
会对 query
percent-encode set 或 special-query percent-encode set 中的字符进行百分比编码(取决于
URL 是否 特殊)。
一个 URLSearchParams
对象具有一个关联的:
一个具有非 null URL 对象 的 URLSearchParams
对象有可能以与 URL
对象的 search
和 hash
setter 等效的方式更改该对象的 路径。
要 初始化 一个 URLSearchParams
对象 query 使用 init:
要 更新 一个 URLSearchParams
对象 query:
delete(name, value)
方法步骤是:
has(name, value)
方法步骤是:
set(name, value)
方法步骤是:
在 URLSearchParams
对象中对名称-值元组进行排序是有用的,特别是为了增加缓存命中率。这可以通过调用 sort()
方法实现:
const url = new URL( "https://example.org/?q=🏳️🌈&key=e1f7bc78" );
url. searchParams. sort();
url. search; // "?key=e1f7bc78&q=%F0%9F%8F%B3%EF%B8%8F%E2%80%8D%F0%9F%8C%88"
为了避免修改原始输入,例如用于比较,可以构造一个新的 URLSearchParams
对象:
const sorted = new URLSearchParams( url. search)
sorted. sort()
要迭代的值对 是 this 的 列表 的 元组,键为名称,值为值。
6.3. 其他标准中的 URL API
一个公开 URL 的标准,应当将 URL 作为字符串公开(通过 序列化 内部的 URL)。标准不应当通过 URL 使用 URL
对象。 URL
对象是用于 URL 操作的。在 IDL 中应当使用 USVString
类型。
这里的高层次概念是值应当作为不可变的数据结构公开。
如果某标准决定使用 "URL" 名称的变体来定义其功能,建议将此功能命名为 "url"(即小写,并且以 "l" 结尾)。不应使用 "URL"、"URI" 和 "IRI" 等名称。然而,如果名称是复合词,建议使用 "URL"(即大写),例如 "newURL" 和 "oldURL"。
接口 EventSource
和 HashChangeEvent
是 HTML 中正确命名的示例。 [HTML]
致谢
多年来,许多人帮助使 URL 变得更加互操作,从而推动了本标准的目标。同样,许多人帮助将本标准发展成今天的样子。
在此,特别感谢以下人士: 100の人, Adam Barth, Addison Phillips, Adrián Chaves, Adrien Ricciardi, Albert Wiersch, Alex Christensen, Alexis Hunt, Alexandre Morgaut, Alexis Hunt, Alwin Blok, Andrew Sullivan, Arkadiusz Michalski, Behnam Esfahbod, Bobby Holley, Boris Zbarsky, Brad Hill, Brandon Ross, Cailyn Hansen, Chris Dumez, Chris Rebert, Corey Farwell, Dan Appelquist, Daniel Bratell, Daniel Stenberg, David Burns, David Håsäther, David Sheets, David Singer, David Walp, Domenic Denicola, Emily Schechter, Emily Stark, Eric Lawrence, Erik Arvidsson, Gavin Carothers, Geoff Richards, Glenn Maynard, Gordon P. Hemsley, hemanth, Henri Sivonen, Ian Hickson, Ilya Grigorik, Italo A. Casas, Jakub Gieryluk, James Graham, James Manger, James Ross, Jeff Hodges, Jeffrey Posnick, Jeffrey Yasskin, Joe Duarte, Joshua Bell, Jxck, Karl Wagner, Kemal Zebari, 田村健人 (Kent TAMURA), Kevin Grandon, Kornel Lesiński, Larry Masinter, Leif Halvard Silli, Mark Amery, Mark Davis, Marcos Cáceres, Marijn Kruisselbrink, Martin Dürst, Mathias Bynens, Matt Falkenhagen, Matt Giuca, Michael Peick, Michael™ Smith, Michal Bukovský, Michel Suignard, Mikaël Geljić, Noah Levitt, Peter Occil, Philip Jägenstedt, Philippe Ombredanne, Prayag Verma, Rimas Misevičius, Robert Kieffer, Rodney Rehm, Roy Fielding, Ryan Sleevi, Sam Ruby, Sam Sneddon, Santiago M. Mola, Sebastian Mayr, Simon Pieters, Simon Sapin, Steven Vachon, Stuart Cook, Sven Uhlig, Tab Atkins, 吉野剛史 (Takeshi Yoshino), Tantek Çelik, Tiancheng "Timothy" Gu, Tim Berners-Lee, 簡冠庭 (Tim Guan-tin Chien), Titi_Alone, Tomek Wytrębowicz, Trevor Rowbotham, Tristan Seligmann, Valentin Gosu, Vyacheslav Matva, Wei Wang, Wolf Lammen, 山岸和利 (Yamagishi Kazutoshi), Yongsheng Zhang, 成瀬ゆい (Yui Naruse),以及 zealousidealroll, 你们真棒!
本标准由 Anne van Kesteren 编写(Apple,annevk@annevk.nl)。
知识产权
版权 © WHATWG (Apple, Google, Mozilla, Microsoft)。此作品依据 知识共享署名 4.0 国际许可协议 授权。若本作品的部分内容被纳入源代码,则源代码部分依据 BSD 3-Clause 许可协议 授权。
这是现行标准。对专利审核版感兴趣的用户可以查看 现行标准审核草案。