URL

现行标准 — 最后更新

参与:
GitHub whatwg/url (新问题, 打开的问题)
在Matrix上聊天
提交:
GitHub whatwg/url/commits
此提交时的快照
@urlstandard
测试:
web-platform-tests url/ (正在进行的工作)
翻译 (非规范性):
日本語
简体中文

摘要

URL标准定义了URL、域名、IP地址、application/x-www-form-urlencoded格式及其API。

目标

URL标准通过以下方法使URL完全互操作:

随着编辑们对该主题的深入了解,目标的范围可能会有所扩大。

1. 基础设施

本规范依赖于Infra[INFRA]

本规范中使用的一些术语在以下标准和规范中定义:


序列化一个整数,将其表示为最短的十进制数。

1.1. 写入

验证错误 表示输入与有效输入不匹配。鼓励用户代理,尤其是符合性检查器,在某处报告这些错误。

验证错误并不意味着解析器终止。解析器的终止总是明确说明,例如,通过返回语句。

标记验证错误是有用的,因为错误处理可能不直观,遗留的用户代理可能未正确实现错误处理,所写内容的意图对其他开发者可能不清楚。

错误类型 错误描述 失败
IDNA
域名到ASCII

Unicode ToASCII 记录错误或返回空字符串。 [UTS46]

如果记录了关于Unicode ToASCII的错误,鼓励用户代理将这些信息传递出去。

域名到Unicode

Unicode ToUnicode 记录错误。[UTS46]

域名到ASCII相同的注意事项适用。

·
主机解析
域名无效代码点

输入的主机包含禁止的域名代码点

当URL是特殊的时,主机部分在处理之前会被百分比解码,这会导致主机部分变为"exa#mple.org",从而触发此错误。

"https://exa%23mple.org"

主机无效代码点

不透明主机(在不是特殊的URL中)包含禁止的主机代码点

"foo://exa[mple.org"

IPv4空部分

IPv4地址以U+002E (.)结尾。

"https://127.0.0.1./"

·
IPv4部分太多

IPv4地址不包含正好4个部分。

"https://1.2.3.4.5/"

IPv4非数字部分

IPv4地址的部分不是数字。

"https://test.42"

IPv4非十进制部分

IPv4地址包含用十六进制或八进制表示的数字。

"https://127.0.0x0.1"

·
IPv4部分超出范围

IPv4地址的部分超过255。

"https://255.255.4000.1"


(仅适用于最后一部分)
IPv6未关闭

IPv6地址缺少关闭的U+005D (])。

"https://[::1"

IPv6无效压缩

IPv6地址以不正确的压缩开头。

"https://[:1]"

IPv6部分太多

IPv6地址包含超过8个部分。

"https://[1:2:3:4:5:6:7:8:9]"

IPv6多重压缩

IPv6地址在多个位置被压缩。

"https://[1::1::1]"

IPv6无效代码点

IPv6地址包含一个既不是ASCII十六进制数字也不是U+003A (:)的代码点。或者意外结束。

"https://[1:2:3!:4]"

"https://[1:2:3:]"

IPv6部分太少

未压缩的IPv6地址包含少于8个部分。

"https://[1:2:3]"

IPv4在IPv6中的部分太多

IPv6地址IPv4地址语法:IPv6地址包含超过6个部分。

"https://[1:1:1:1:1:1:1:127.0.0.1]"

IPv4在IPv6中的无效代码点

IPv6地址IPv4地址语法:

  • IPv4部分为空或包含非ASCII数字
  • IPv4部分包含前导0。
  • IPv4部分太多。

"https://[ffff::.0.0.1]"

"https://[ffff::127.0.xyz.1]"

"https://[ffff::127.0xyz]"

"https://[ffff::127.00.0.1]"

"https://[ffff::127.0.0.1.2]"

IPv4在IPv6中的部分超出范围

IPv6地址IPv4地址语法:IPv4部分超过255。

"https://[ffff::127.0.0.4000]"

IPv4在IPv6中的部分太少

IPv6地址IPv4地址语法:IPv4地址包含部分太少。

"https://[ffff::127.0.0]"

URL解析
无效URL单元

发现的代码点不是URL单元

"https://example.org/>"

" https://example.org "

"ht
tps://example.org
"

"https://example.org/%s"

·
特殊方案缺少后续的斜线

输入的方案后面没有"//"。

"file:c:/my-secret-folder"

"https:example.org"

const url = new URL("https:foo.html", "https://example.org/");
·
缺少方案的非相对URL

输入缺少方案,因为它不是以ASCII字母开头,并且未提供基础URL,或基础URL不能作为基础URL使用,因为它有一个不透明路径

输入的方案缺失,且未给出基础URL

const url = new URL("💩");

输入的方案缺失,但基础URL有一个不透明路径

const url = new URL("💩", "mailto:user@example.org");
无效反斜线

URL有一个特殊方案,并且它使用U+005C (\)而不是U+002F (/)。

"https://example.org\path\to\file"

·
无效凭证

输入包含凭证

"https://user@example.org"

"ssh://user@example.org"

·
主机缺失

输入有一个特殊方案,但不包含主机

"https://#fragment"

"https://:443"

"https://user:pass@"

端口超出范围

输入的端口太大。

"https://example.org:70000"

无效端口

输入的端口无效。

"https://example.org:7z"

文件无效的Windows驱动器号

输入是相对URL字符串,其以Windows驱动器号开头,并且基础URL方案是"file"。

const url = new URL("/c:/path/to/file", "file:///c:/");
·
文件无效的Windows驱动器号主机

file:URL的主机是Windows驱动器号。

"file://c:"

·

1.2. 解析器

EOF 代码点是一个概念性的代码点,表示字符串或代码点流的结束。

对于一个字符串输入指针是一个整数,指向输入中的代码点。最初它指向输入的开始。如果它为-1,则表示没有指向。如果它大于或等于输入代码点长度,则指向EOF代码点

当使用指针时,c引用指针指向的代码点,只要它没有指向空处。当指针指向空处时,c无法使用。

当使用指针时,remaining引用从指针+1到字符串末尾的代码点子串,只要c不是EOF代码点。当cEOF代码点时,remaining无法使用。

如果"mailto:username@example"是正在处理的字符串,并且指针指向@,c是U+0040 (@),而remaining是"example"。

如果正在处理的是空字符串,并且指针指向开始然后减1,使用cremaining将会出错。

1.3. 百分比编码字节

百分比编码字节是U+0025 (%),后跟两个ASCII十六进制数字

通常,对于百分比编码字节的序列,最好在百分比解码后再传递给UTF-8解码,无BOM或失败,以确保不会失败。其重要性取决于百分比编码字节的使用位置。例如,对于主机解析器,不遵循此建议是致命的,而对于URL渲染,这些百分比编码字节将不会百分比解码后呈现。

百分比编码一个字节byte,返回由U+0025 (%),后跟表示byte的两个ASCII大写十六进制数字组成的字符串

百分比解码一个字节序列input,请按以下步骤操作:

input包含非ASCII字节时,使用除UTF-8解码,无BOM外的任何内容都可能不安全,不推荐使用。

  1. output为一个空的字节序列

  2. 对于input中的每个字节byte

    1. 如果byte不是0x25 (%),则将byte附加到output

    2. 否则,如果byte是0x25 (%),并且inputbyte之后的两个字节不在0x30 (0)到0x39 (9),0x41 (A)到0x46 (F),以及0x61 (a)到0x66 (f)的范围内,则将byte附加到output

    3. 否则:

      1. bytePointinputbyte之后的两个字节,解码后,解释为十六进制数。

      2. 将值为bytePoint的字节附加到output

      3. 跳过input中的下两个字节。

  3. 返回output

百分比解码一个标量值字符串input

  1. bytesinputUTF-8编码

  2. 返回bytes百分比解码结果。

通常,百分比编码会产生包含更多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):

  1. encoder成为从encoding获取编码器的结果。

  2. input转换为I/O队列,并称之为inputQueue

  3. output为空字符串。

  4. potentialError为0。

    这需要是一个非null的值,以启动后续的循环。

  5. potentialError不为null时:

    1. encodeOutput为空的I/O队列

    2. potentialError设置为运行编码或失败的结果,使用inputQueueencoderencodeOutput

    3. 对于encodeOutput中的每个转换为字节序列的byte

      1. 如果spaceAsPlus为true并且byte是0x20 (SP),则将U+002B (+)附加到output继续

      2. isomorph成为一个代码点,其byte

      3. 断言:percentEncodeSet包含所有非ASCII代码点

      4. 如果isomorph不在percentEncodeSet中,则将isomorph附加到output

      5. 否则,百分比编码byte并将结果附加到output

    4. 如果potentialError不为null,则附加"%26%23",后跟表示potentialError的以十进制表示的ASCII数字的最短序列,再附加"%3B"到output

      encoding不是UTF-8时,这可能发生。

  6. 返回output

对于percentEncodeSet参数的可能值中,只有两个会对U+0025 (%)进行编码,从而产生“可往返的数据”:组件百分比编码集application/x-www-form-urlencoded百分比编码集。其他用于percentEncodeSet参数的值(如用于URL解析器的)将保持U+0025 (%)不变,因此首先需要对其进行UTF-8百分比编码以正确表示。

UTF-8百分比编码一个标量值scalarValue,使用一个percentEncodeSet,返回运行编码后百分比编码的结果,使用UTF-8scalarValue作为字符串,以及percentEncodeSet

UTF-8百分比编码一个标量值字符串input,使用一个percentEncodeSet,返回运行编码后百分比编码的结果,使用UTF-8input,以及percentEncodeSet


以下是上述操作的总结示例:

操作 输入 输出
百分比编码input 0x23 "%23"
0x7F "%7F"
百分比解码input `%25%s%1G` `%%s%1G`
百分比解码input "‽%25%2E" 0xE2 0x80 0xBD 0x25 0x2E
编码后百分比编码,使用Shift_JISinput用户信息百分比编码集 " " "%20"
"" "%81%DF"
"" "%26%238253%3B"
编码后百分比编码,使用ISO-2022-JPinput用户信息百分比编码集 "¥" "%1B(J\%1B(B"
编码后百分比编码,使用Shift_JISinput用户信息百分比编码集,并设置为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]

在将URLA传递给B时,双方都需要仔细考虑发生的情况。A可能会泄露它不希望泄露的数据。B可能会接收到意料之外的输入,并采取对用户有害的行动。尤其是,B永远不应信任A,因为在某些情况下,来自AURL可能来自不受信任的来源。

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.comexample.com. 这两个域名并不相同,通常被视为不同的域名。

IP 地址是一个IPv4 地址IPv6 地址

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。

要获取 主机 host公共后缀,请运行以下步骤。这些步骤返回 null 或表示 host 的一部分域名,该部分包含在公共后缀列表中。[PSL]

  1. 如果 host 不是域名,则返回 null。

  2. 如果 host.”结尾,则让 trailingDot 为“.”;否则为空字符串。

  3. 通过运行公共后缀列表算法,以 host 作为域名,确定 publicSuffix[PSL]

  4. 断言:publicSuffix 是不以“.结尾ASCII 字符串

  5. 返回 publicSuffixtrailingDot 连接的结果。

要获取 主机 host可注册域名,请运行以下步骤。这些步骤返回 null 或由 host公共后缀及其前面的域标签(如果有)构成的域名

  1. 如果 host公共后缀为 null 或 host公共后缀等于 host,则返回 null。

  2. 如果 host.”结尾,则让 trailingDot 为“.”;否则为空字符串。

  3. 通过运行公共后缀列表算法,以 host 作为域名,确定 registrableDomain[PSL]

  4. 断言:registrableDomain 是不以“.结尾ASCII 字符串

  5. 返回 registrableDomaintrailingDot 连接的结果。

主机输入 公共后缀 可注册域名
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的情况下,执行以下步骤:

  1. 运行Unicode ToASCIIdomain_name设为domainUseSTD3ASCIIRules设为beStrictCheckHyphens设为 false,CheckBidi设为 true,CheckJoiners设为 true,Transitional_Processing设为 false,VerifyDnsLength设为beStrict,并将结果赋值给result[UTS46]

    如果beStrict为 false,domainASCII 字符串,并且严格分割 domain上的 U+002E (.) 不会产生任何,且该项不会ASCII 不区分大小写的"xn--"开头,那么该步骤等价于对domain进行ASCII 小写化

  2. 如果result是失败值,触发域名到 ASCII验证错误,并返回失败。

  3. 如果result是空字符串,触发域名到 ASCII验证错误,并返回失败。

  4. 返回result

本文档和整个 Web 平台使用的是Unicode IDNA 兼容性处理,而不是 IDNA2008。例如,☕.example变成xn--53h.example,而不是失败。[UTS46][RFC5890]

域名到 Unicode算法,在给定域名domain和布尔值beStrict的情况下,执行以下步骤:

  1. 运行Unicode ToUnicodedomain_name设为domainCheckHyphens设为 false,CheckBidi设为 true,CheckJoiners设为 true,UseSTD3ASCIIRules设为beStrictTransitional_Processing设为 false,并将结果赋值给result[UTS46]

  2. 标记域名到 Unicode验证错误,然后返回result

3.4. 主机写入

有效主机字符串必须是有效域名字符串有效的 IPv4 地址字符串,或:U+005B ([),后跟一个有效的 IPv6 地址字符串,再加上 U+005D (])。

如果这些步骤返回成功,则domain有效域名

  1. 运行域名到 ASCII算法,使用domain和 true。

  2. 如果result为失败,则返回失败。

  3. result设置为运行域名到 Unicode算法的结果,使用result和 true。

  4. 如果result包含任何错误,则返回失败。

  5. 返回成功。

理想情况下,我们应该根据组成有效域名的代码点序列定义此内容,而不是使用逐个消除的方法:issue 245

有效域名字符串必须是一个有效域名的字符串。

有效 IPv4 地址字符串必须是四个最短的可能由ASCII 数字组成的字符串,表示范围为 0 到 255(含)的十进制数,并由 U+002E (.) 分隔。

有效 IPv6 地址字符串IP 版本 6 地址架构的"地址的文本表示"章节中定义。[RFC4291]

有效的不透明主机字符串必须是以下之一:

这不是有效主机字符串定义的一部分,因为它需要在特定上下文中进行区分。

3.5. 主机解析

主机解析器接受一个标量值字符串 input,并带有可选的布尔值isOpaque(默认为 false),然后执行以下步骤。它们返回失败或主机

  1. 如果input以 U+005B ([) 开始,那么:

    1. 如果input不以 U+005D (]) 结束,IPv6 未关闭 验证错误,返回失败。

    2. 返回运行IPv6 解析的结果,移除其前导的 U+005B ([) 和尾随的 U+005D (])。

  2. 如果isOpaque为 true,则返回运行不透明主机解析 input的结果。

  3. 断言:input不是空字符串。

  4. 运行无 BOM 的 UTF-8 解码,对input进行百分号解码的结果,得到domain

    或者,可以使用无 BOM 的 UTF-8 解码或失败,加上早期返回失败的操作,因为域名到 ASCII在遇到 U+FFFD (�) 时会失败。

  5. 运行域名到 ASCII算法,使用domain和 false,得到asciiDomain

  6. 如果asciiDomain为失败,则返回失败。

  7. 如果asciiDomain包含禁止的域名代码点域名无效代码点 验证错误,返回失败。

  8. 如果asciiDomain以数字结尾,则返回运行IPv4 解析 asciiDomain的结果。

  9. 返回asciiDomain


结尾为数字检查器接受一个ASCII 字符串input,然后运行以下步骤。它们返回一个布尔值。

  1. partsinput通过 U+002E (.) 严格分割的结果。

  2. 如果parts中的最后一个是空字符串,则:

    1. 如果parts大小为1,则返回false。

    2. 移除parts的最后一项。

  3. lastparts中的最后一个

  4. 如果last非空且仅包含ASCII 数字,则返回 true。

    错误输入"09"将在稍后的IPv4 解析器中捕获。

  5. 如果将last解析为IPv4 数字不返回失败,则返回 true。

    这相当于检查last是否为"0X"或"0x",后跟零个或多个ASCII 十六进制数字

  6. 返回 false。

IPv4 解析器接受一个ASCII 字符串input,然后运行以下步骤。它们返回失败或一个IPv4 地址

不要直接调用IPv4 解析器。而是检查主机解析器的返回值是否为IPv4 地址

  1. partsinput通过 U+002E (.) 严格分割的结果。

  2. 如果parts的最后一项是空字符串,则:

    1. IPv4 空项 验证错误

    2. 如果parts大小大于1,则移除parts的最后一项。

  3. 如果parts大小大于4,IPv4 过多项 验证错误,返回失败。

  4. numbers成为一个空的列表

  5. 对每个partparts

    1. result解析part的结果。

    2. 如果result是失败,IPv4 非数字项 验证错误,返回失败。

    3. 如果result[1]为 true,IPv4 非十进制项 验证错误

    4. result[0] 添加到numbers

  6. 如果numbers中的任何项大于255,IPv4 超出范围项 验证错误

  7. 如果numbers中除了最后一项之外的任何项大于255,则返回失败。

  8. 如果numbers中的最后一项大于等于 256(5 − numbers大小),则返回失败。

  9. ipv4成为numbers中的最后一项。

  10. 移除numbers中的最后一项。

  11. counter为0。

  12. 对每个numbersn

    1. 通过n × 256(3 − counter)增加ipv4

    2. counter加1。

  13. 返回ipv4

IPv4 数字解析器接受一个ASCII 字符串input,然后运行以下步骤。它们返回失败或一个元组,包含一个数字和一个布尔值。

  1. 如果input是空字符串,则返回失败。

  2. validationError为 false。

  3. R为10。

  4. 如果input包含至少两个代码点,并且前两个代码点是"0X"或"0x",则:

    1. validationError设置为 true。

    2. input中移除前两个代码点。

    3. R设置为16。

  5. 否则,如果input包含至少两个代码点,并且第一个代码点是 U+0030 (0),则:

    1. validationError设置为 true。

    2. input中移除第一个代码点。

    3. R设置为8。

  6. 如果input是空字符串,则返回(0, true)。

  7. 如果input包含不是基数R的数字的代码点,则返回失败。

  8. outputinput表示的基数R记数法中的整数值,使用ASCII 十六进制数字表示0到15之间的数字。

  9. 返回(output, validationError)。


IPv6 解析器接受一个标量值字符串input,然后运行以下步骤。它们返回失败或一个IPv6 地址

IPv6 解析器理论上可以直接调用,但请先与本文件的编辑讨论。

  1. address成为一个新的IPv6 地址,其IPv6 片段均为0。

  2. pieceIndex为0。

  3. compress为null。

  4. pointer成为input指针

  5. 如果c是 U+003A (:),则:

    1. 如果剩余部分不以 U+003A (:) 开始,IPv6-无效压缩 验证错误,返回失败。

    2. pointer增加2。

    3. pieceIndex增加1,然后将compress设置为pieceIndex

  6. c不是EOF 代码点时:

    1. 如果pieceIndex为8,IPv6 片段过多 验证错误,返回失败。

    2. 如果c是 U+003A (:),则:

      1. 如果compress非空,IPv6 多重压缩 验证错误,返回失败。

      2. pointerpieceIndex分别增加1,将compress设置为pieceIndex,然后继续
    3. valuelength为0。

    4. length小于4并且cASCII 十六进制数字时,将value设置为value × 0x10 + c作为十六进制数解释,并将pointerlength分别增加1。

    5. 如果c是 U+002E (.),则:

      1. 如果length为0,IPv4 在 IPv6 中无效代码点 验证错误,返回失败。

      2. pointer减少length

      3. 如果pieceIndex大于6,IPv4 在 IPv6 中片段过多 验证错误,返回失败。

      4. numbersSeen为0。

      5. c不是EOF 代码点时:

        1. ipv4Piece为null。

        2. 如果numbersSeen大于0,则:

          1. 如果c是 U+002E (.) 并且numbersSeen小于4,则将pointer增加1。

          2. 否则,IPv4 在 IPv6 中无效代码点 验证错误,返回失败。
        3. 如果c不是ASCII 数字IPv4 在 IPv6 中无效代码点 验证错误,返回失败。

        4. cASCII 数字时:

          1. number成为c作为十进制数解释。

          2. 如果ipv4Piece为 null,则将ipv4Piece设置为number

            否则,如果ipv4Piece为0,IPv4 在 IPv6 中无效代码点 验证错误,返回失败。

            否则,将ipv4Piece设置为ipv4Piece × 10 + number

          3. 如果ipv4Piece大于255,IPv4 在 IPv6 中超出范围的片段 验证错误,返回失败。

          4. pointer增加1。

        5. address[pieceIndex]设置为address[pieceIndex] × 0x100 + ipv4Piece

        6. numbersSeen增加1。

        7. 如果numbersSeen为2或4,则将pieceIndex增加1。

      6. 如果numbersSeen不是4,IPv4 在 IPv6 中片段过少 验证错误,返回失败。

      7. 跳出

    6. 否则,如果c是 U+003A (:):

      1. pointer增加1。

      2. 如果cEOF 代码点IPv6 无效代码点 验证错误,返回失败。

    7. 否则,如果c不是EOF 代码点IPv6 无效代码点 验证错误,返回失败。

    8. address[pieceIndex]设置为value

    9. pieceIndex增加1。

  7. 如果compress非空,则:

    1. swapspieceIndexcompress

    2. pieceIndex设置为7。

    3. pieceIndex不为0且swaps大于0时,交换address[pieceIndex]与address[compress + swaps − 1],然后将pieceIndexswaps均减少1。

  8. 否则,如果compress为空并且pieceIndex不为8,IPv6 片段过少 验证错误,返回失败。

  9. 返回address


不透明主机解析器接受一个标量值字符串input,然后运行以下步骤。它们返回失败或一个不透明主机

  1. 如果input包含禁止的主机代码点主机无效代码点验证错误,返回失败。

  2. 如果input包含一个不是URL代码点且不是U+0025 (%) 的代码点无效URL单位验证错误

  3. 如果input包含一个U+0025 (%) 且其后的两个代码点不是ASCII十六进制数字无效URL单位验证错误

  4. 返回使用C0控制百分比编码集input运行UTF-8百分比编码的结果。

3.6. 主机序列化

主机序列化器接受一个主机host,然后运行以下步骤。它们返回一个ASCII字符串

  1. 如果host是一个IPv4地址,返回对host运行IPv4序列化器的结果。

  2. 否则,如果host是一个IPv6地址,返回U+005B ([),后跟对host运行IPv6序列化器的结果,最后跟U+005D (])。

  3. 否则,host是一个域名不透明主机空主机,返回host

IPv4序列化器接受一个IPv4地址address,然后运行以下步骤。它们返回一个ASCII字符串

  1. output为空字符串。

  2. naddress的值。

  3. 对于每个在范围1到4(含)的i

    1. n % 256,序列化的值,前置到output

    2. 如果i不是4,则将U+002E (.)前置到output

    3. n设置为floor(n / 256)。

  4. 返回output

IPv6序列化器接受一个IPv6地址address,然后运行以下步骤。它们返回一个ASCII字符串

  1. output为空字符串。

  2. compress为指向address中第一段0最长序列的第一个IPv6片段的索引。

    0:f:0:0:f:f:0:0中,它指向第二个0。

  3. 如果address中没有比长度为1更长的0序列,则将compress设置为null。

  4. ignore0为false。

  5. 对于每个在范围0到7(含)的pieceIndex

    1. 如果ignore0为true且address[pieceIndex]为0,则继续

    2. 否则,如果ignore0为true,设置ignore0为false。

    3. 如果compresspieceIndex,则:

      1. separator为"::",如果pieceIndex为0,否则为U+003A (:)。

      2. separator追加到output

      3. 设置ignore0为true,然后继续

    4. address[pieceIndex]表示为最短的小写十六进制数,追加到output

    5. 如果pieceIndex不是7,则将U+003A (:)追加到output

  6. 返回output

该算法需要遵循[RFC5952]中的建议。

3.7. 主机等效性

要确定主机A是否等于主机B,如果AB,则返回true,否则返回false。

证书比较需要一个忽略域名末尾点(如果有的话)的主机等效性检查。然而,这些主机还强制执行了诸如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

基址和输出的URL为简洁起见以序列化的形式表示。

4.1. URL 表示

URL 是一个结构体,表示一个通用标识符。为了与有效的URL字符串区分开来,它也可以被称为URL记录

URL方案是一个ASCII字符串,用于标识URL的类型,并可以用于在解析后进一步处理URL。它最初为空字符串。

URL用户名是一个ASCII字符串,用于标识用户名。它最初为空字符串。

URL密码是一个ASCII字符串,用于标识密码。它最初为空字符串。

URL主机为null或主机。最初为null。

下表列出了允许的URL方案/主机组合。

方案 主机
域名 IPv4地址 IPv6地址 不透明主机 空主机 null
特殊方案,不包括"file"
"file"
其他

URL端口为null或标识网络端口的16位无符号整数。最初为null。

URL路径是一个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包含凭证,如果它的用户名密码不是空字符串。

URL具有不透明路径,如果它的路径URL路径段

URL如果其主机为null或为空字符串,或者其方案是"file" ,则不能具有用户名/密码/端口

URL可以被指定为基础URL

基础URL对于URL解析器很有用,当输入可能是相对URL字符串时。


Windows 驱动器号 是两个码点,第一个是 ASCII 字母,第二个是 U+003A (:) 或 U+007C (|)。

规范化的 Windows 驱动器号Windows 驱动器号,其中第二个码点是 U+003A (:)。

根据 URL 写入 部分,只有 规范化的 Windows 驱动器号 是合规的。

如果符合以下所有条件,则字符串 以 Windows 驱动器号开头

字符串 以 Windows 驱动器号开头
"c:"
"c:/"
"c:a"

缩短 url 的路径

  1. 断言url 没有不透明路径

  2. pathurl路径

  3. 如果 url方案是 "file",path大小为1,并且 path[0] 是规范化的 Windows 驱动器号,则返回。

  4. 移除 path 的最后一项(如果有)。

4.3. URL 写入

有效 URL 字符串 必须是 相对 URL 带片段字符串绝对 URL 带片段字符串之一。

绝对 URL 带片段字符串 必须是 绝对 URL 字符串,可选地后跟 U+0023 (#) 和 URL 片段字符串

绝对 URL 字符串 必须是以下之一:

任何可选地后跟 U+003F (?) 和 URL 查询字符串

URL 方案字符串 必须是一个 ASCII 字母,后跟零个或多个 ASCII 字母数字字符、U+002B (+)、U+002D (-) 和 U+002E (.)。

相对 URL 带片段字符串 必须是 相对 URL 字符串,可选地后跟 U+0023 (#) 和 URL 片段字符串

相对 URL 字符串 必须是以下之一,基于 基本 URL方案进行切换:

一个 特殊方案,不是 "file"

一个 方案相对特殊 URL 字符串

一个 路径绝对 URL 字符串

一个 路径相对无方案 URL 字符串

"file"

一个 方案相对文件 URL 字符串

如果 基本 URL主机是空主机,则一个 路径绝对 URL 字符串

如果 基本 URL主机不是空主机,则一个 非 Windows 文件路径绝对 URL 字符串

一个 路径相对无方案 URL 字符串

否则

一个 方案相对 URL 字符串

一个 路径绝对 URL 字符串

一个 路径相对无方案 URL 字符串

可选地后跟 U+003F (?) 和 URL 查询字符串

解析相对 URL 字符串时,需要非空的 基本 URL

方案相对特殊 URL 字符串 必须是 "//",后跟一个 有效主机字符串,可选地后跟 U+003A (:) 和 URL 端口字符串,再可选地后跟 绝对路径 URL 字符串

URL 端口字符串 必须是以下之一:

方案相对 URL 字符串 必须是 "//",后跟 不透明主机和端口字符串,可选地后跟 绝对路径 URL 字符串

不透明主机和端口字符串 必须是空字符串或: 有效的不透明主机字符串,可选地后跟 U+003A (:) 和 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&ouml;rg&aring;sbord">Test</a>

由于文档编码为 windows-1252,链接的 URL查询字符串 将是 "sm%F6rg%E5sbord"。如果文档编码是 UTF-8,它将是 "sm%C3%B6rg%C3%A5sbord"。

URL 单位URL 代码点百分号编码字节

百分号编码字节 可用于编码不属于 URL 代码点 或被排除写入的代码点。


无法在 有效的 URL 字符串 中表示 用户名密码

4.4. URL 解析

URL 解析器 接受一个标量值字符串 input,带有可选的 null 或 基本 URL base(默认为 null)和可选的 编码 encoding(默认为 UTF-8),并运行以下步骤:

非网页浏览器的实现只需实现基本 URL 解析器

本标准不包括浏览器地址栏中用户输入转换为URL 记录的过程。标准确实包含了 URL 渲染要求,因为它们涉及信任决策。

  1. url 为在 input 上使用 baseencoding 运行 基本 URL 解析器 的结果。

  2. 如果 url 为失败,返回失败。

  3. 如果 url方案 不是 "blob",返回 url

  4. urlblob URL 条目 设置为 解析 blob URL url 的结果, 如果未返回失败,则为 null。

  5. 返回 url


基本 URL 解析器 接受一个标量值字符串 input,带有可选的 null 或基本 URL base(默认为 null),可选的 编码 encoding (默认为 UTF-8), 可选的 URL url, 以及一个可选的状态覆盖 state override, 然后运行以下步骤:

参数 encoding 是一个仅与 HTML 相关的遗留概念。urlstate override 参数仅用于某些 API。[HTML]

当没有传递 urlstate override 参数时,基本 URL 解析器 返回一个新的 URL 或失败。如果传递了它们, 该算法将修改传递的 url,并且可能在没有返回结果的情况下终止。

  1. 如果没有给出 url

    1. url 设置为一个新的 URL

    2. 如果 input 包含任何前导或尾随的C0 控制或空格,则触发 无效的 URL 单元 验证错误

    3. input 中移除任何前导和尾随的 C0 控制或空格

  2. 如果 input 包含任何ASCII 制表符或换行符,则触发 无效的 URL 单元 验证错误

  3. input 中移除所有 ASCII 制表符或换行符

  4. state 设置为 state override(如果给定),否则为 scheme start state

  5. encoding 设置为从 encoding 中获取输出编码的结果。

  6. buffer 设为空字符串。

  7. atSignSeeninsideBracketspasswordTokenSeen 设置为 false。

  8. input 创建一个 指针 pointer

  9. 通过切换 state 来持续运行以下状态机。如果在运行之后 pointer 指向EOF 码点,进入下一步。否则,增加 pointer 的值 1,并继续状态机。

    方案开始状态
    1. 如果 cASCII 字母,将小写后的 c 追加到 buffer,并将 state 设置为 方案状态

    2. 否则,如果没有给定 state override,将 state 设置为 无方案状态,并将 pointer 减少 1。

    3. 否则,返回失败。

      此失败仅供 Location 对象的 protocol setter 使用。

    方案状态
    1. 如果 cASCII 字母或数字,U+002B (+)、U+002D (-) 或 U+002E (.),将小写后的 c 追加到 buffer

    2. 否则,如果 c 是 U+003A (:),那么:

      1. 如果给定了 state override,则:

        1. 如果 url方案特殊方案,并且 buffer 不是 特殊方案,则返回。

        2. 如果 url方案 不是 特殊方案,并且 buffer特殊方案,则返回。

        3. 如果 url 包含凭证 或者有非空的 端口,并且 buffer 是 "file",则返回。

        4. 如果 url方案 是 "file",并且它的 主机 是一个 空主机,则返回。

      2. url方案 设置为 buffer

      3. 如果给定了 state override,则:

        1. 如果 url端口url方案默认端口,则将 url端口 设置为 null。

        2. 返回。

      4. buffer 设为空字符串。

      5. 如果 url方案 是 "file",那么:

        1. 如果 剩余 不以 "//" 开头,触发 特殊方案缺少后续斜杠 验证错误

        2. state 设置为 文件状态

      6. 否则,如果 url 是特殊的base 非空,并且 base方案url 的方案相同:

        1. 断言base 是特殊的(因此它没有 不透明路径)。

        2. state 设置为 特殊相对或权限状态

      7. 否则,如果 url 是特殊的,将 state 设置为 特殊权限斜杠状态

      8. 否则,如果 剩余 以 U+002F (/) 开头,设置 state路径或权限状态,并将 pointer 增加 1。

      9. 否则,将 url路径 设置为空字符串,并将 state 设置为 不透明路径状态

    3. 否则,如果没有给定 state override,将 buffer 设为空字符串,state 设为 无方案状态,并重新开始(从 input 中的第一个字符开始)。

    4. 否则,返回失败。

      此失败仅供 Location 对象的 protocol setter 使用。此外,方案状态中定义的非失败退出是专门为了定义 setter。

    无方案状态
    1. 如果 base 为空,或者 base 有一个 不透明路径,并且 c 不是 U+0023 (#),触发 缺少方案的非相对URL 验证错误,返回失败。

    2. 否则,如果 base 有一个 不透明路径,并且 c 是 U+0023 (#),将 url方案 设置为 base方案,将 url路径 设置为 base路径,将 url查询 设置为 base查询,将 url片段 设置为空字符串,并 将 state 设置为 片段状态

    3. 否则,如果 base方案 不是 "file",将 state 设置为 相对状态, 并将 pointer 减少 1。

    4. 否则,将 state 设置为 文件状态, 并将 pointer 减少 1。

    特殊相对或权限状态
    1. 如果 c 是 U+002F (/) 并且 剩余 以 U+002F (/) 开头,将 state 设置为 特殊权限忽略斜杠状态,并将 pointer 增加 1。

    2. 否则,触发 特殊方案缺少后续斜杠 验证错误,将 state 设置为 相对状态,并将 pointer 减少 1。

    路径或权限状态
    1. 如果 c 是 U+002F (/) ,则将 state 设置为 权限状态

    2. 否则,将 state 设置为 路径状态, 并将 pointer 减少 1。

    相对状态
    1. 断言:base方案 不是 "file"。

    2. url方案 设置为 base方案

    3. 如果 c 是 U+002F (/) ,则将 state 设置为 相对斜杠状态

    4. 否则,如果 url 是特殊的, 并且 c 是 U+005C (\),则触发 无效反斜杠 验证错误,将 state 设置为 相对斜杠状态

    5. 否则:

      1. url用户名 设置为 base用户名url密码 设置为 base密码,将 url主机 设置为 base主机,将 url端口 设置为 base端口,将 url路径 设置为 base路径 的克隆,将 url查询 设置为 base查询

      2. 如果 c 是 U+003F (?) ,将 url查询 设置为空字符串, 并将 state 设置为 查询状态

      3. 否则,如果 c 是 U+0023 (#),将 url片段 设置为空字符串, 并将 state 设置为 片段状态

      4. 否则,如果 c 不是 EOF 代码点

        1. url查询 设置为 null。

        2. 缩短 url路径

        3. state 设置为 路径状态, 并将 pointer 减少 1。

    相对斜杠状态
    1. 如果 url 是特殊的,并且 c 是 U+002F (/) 或 U+005C (\),则:

      1. 如果 c 是 U+005C (\),触发 无效反斜杠 验证错误

      2. state 设置为 特殊权限忽略斜杠状态

    2. 否则,如果 c 是 U+002F (/) ,则将 state 设置为 权限状态

    3. 否则,将 url用户名 设置为 base用户名url密码 设置为 base密码url主机 设置为 base主机url端口 设置为 base端口,将 state 设置为 路径状态,然后减少 pointer 1。

    特殊权限斜杠状态
    1. 如果 c 是 U+002F (/) 并且 剩余部分 以 U+002F (/) 开头,将 state 设置为 特殊权限忽略斜杠状态,并将 pointer 增加 1。

    2. 否则,触发 特殊方案缺少后续斜杠 验证错误,将 state 设置为 特殊权限忽略斜杠状态,并减少 pointer 1。

    特殊权限忽略斜杠状态
    1. 如果 c 既不是 U+002F (/) 也不是 U+005C (\),则将 state 设置为 权限状态,并减少 pointer 1。

    2. 否则,触发 特殊方案缺少后续斜杠 验证错误

    权限状态
    1. 如果 c 是 U+0040 (@),则:

      1. 触发 无效凭证 验证错误

      2. 如果 atSignSeen 为 true,则在 buffer 前面添加 "%40"。

      3. atSignSeen 设置为 true。

      4. 对于 buffer 中的每个 codePoint

        1. 如果 codePoint 是 U+003A (:) 且 passwordTokenSeen 为 false, 则将 passwordTokenSeen 设置为 true,并 继续

        2. codePoint 运行 UTF-8 百分号编码,使用 用户信息百分号编码集

        3. 如果 passwordTokenSeen 为 true,则将 encodedCodePoints 添加到 url密码

        4. 否则,将 encodedCodePoints 添加到 url用户名

      5. buffer 设置为空字符串。

    2. 否则,如果以下之一为 true:

      那么:

      1. 如果 atSignSeen 为 true 且 buffer 为空字符串,触发 缺少主机 验证错误,返回失败。

      2. pointer 减少 buffer代码点长度 + 1,将 buffer 设置为空字符串,并将 state 设置为 主机状态

    3. 否则,将 c 添加到 buffer

    主机状态
    主机名状态
    1. 如果提供了 state override 并且 urlscheme 是 "file",则将 pointer 减少 1,并将 state 设置为 文件主机状态

    2. 否则,如果 c 是 U+003A (:) 且 insideBrackets 为 false,则:

      1. 如果 buffer 为空字符串,主机缺失 验证错误,返回失败。

      2. 如果提供了 state overridestate override主机名状态,则返回。

      3. host 设置为使用 url 非特殊 情况下对 buffer 进行的 主机解析 的结果。

      4. 如果 host 是失败,则返回失败。

      5. url主机 设置为 host,将 buffer 设置为空字符串, 并将 state 设置为 端口状态

    3. 否则,如果以下任意条件为真:

      那么减少 pointer 1,然后:

      1. 如果 url 是特殊的 并且 buffer 为空字符串,主机缺失 验证错误,返回失败。

      2. 否则,如果提供了 state overridebuffer 为空字符串,且 url 包含凭证url端口 不为 null,则返回。

      3. host 设置为使用 url 非特殊 情况下对 buffer 进行的 主机解析 的结果。

      4. 如果 host 是失败,则返回失败。

      5. url主机 设置为 host,将 buffer 设置为空字符串, 并将 state 设置为 路径起始状态

      6. 如果提供了 state override,则返回。

    4. 否则:

      1. 如果 c 是 U+005B ([),则将 insideBrackets 设置为 true。

      2. 如果 c 是 U+005D (]),则将 insideBrackets 设置为 false。

      3. c 添加到 buffer

    端口状态
    1. 如果 cASCII 数字,则将 c 附加到 buffer

    2. 否则,如果以下任一条件为真:

      那么:

      1. 如果 buffer 不为空字符串,则:

        1. port 设置为由 buffer 在基数 10 中表示的数学整数值,使用 ASCII 数字 表示 0 到 9 的值。

        2. 如果 port 大于 216 − 1,端口超出范围 验证错误,返回失败。

        3. url端口 设置为 null,如果 porturlscheme默认端口;否则将其设置为 port

        4. buffer 设置为空字符串。

      2. 如果提供了 state override,则返回。

      3. state 设置为 路径起始状态,并将 pointer 减少 1。

    3. 否则,端口无效 验证错误,返回失败。

    文件状态
    1. urlscheme 设置为 "file"。

    2. url主机 设置为空字符串。

    3. 如果 c 是 U+002F (/) 或 U+005C (\),则:

      1. 如果 c 是 U+005C (\),无效反斜杠 验证错误

      2. state 设置为 文件斜杠状态

    4. 否则,如果 base 非空且 basescheme 是 "file":

      1. url主机 设置为 base主机url路径 设置为 base路径克隆,并将 url查询 设置为 base查询

      2. 如果 c 是 U+003F (?),则将 url查询 设置为空字符串,并将 state 设置为 查询状态

      3. 否则,如果 c 是 U+0023 (#),则将 url片段 设置为空字符串,并将 state 设置为 片段状态

      4. 否则,如果 c 不是 EOF 代码点,则:

        1. url查询 设置为 null。

        2. 如果从 pointerinput 末尾的 代码点子字符串 不以 Windows 驱动器字母 开头,则 缩短 url路径

        3. 否则:

          1. 文件无效 Windows 驱动器字母 验证错误

          2. url路径 设置为 « »。

          这是一个(平台无关的)Windows 驱动器字母怪癖。

        4. state 设置为 路径状态,并将 pointer 减少 1。

    5. 否则,将 state 设置为 路径状态,并将 pointer 减少 1。

    文件斜杠状态
    1. 如果 c 是 U+002F (/) 或 U+005C (\),则:

      1. 如果 c 是 U+005C (\),无效反斜杠 验证错误

      2. state 设置为 文件主机状态

    2. 否则:

      1. 如果 base 非空且 basescheme 是 "file",则:

        1. url主机 设置为 base主机

        2. 如果从 pointerinput 末尾的 代码点子字符串 不以 Windows 驱动器字母 开头,且 base路径[0] 是 标准化 Windows 驱动器字母,则 追加 base路径[0] 到 url路径

          这是一个(平台无关的)Windows 驱动器字母怪癖。

      2. state 设置为 路径状态,并将 pointer 减少 1。

    文件主机状态
    1. 如果 cEOF 代码点、U+002F (/)、 U+005C (\)、U+003F (?) 或 U+0023 (#),则将 pointer 减少 1,然后:

      1. 如果没有给出 state overridebufferWindows 驱动器字母,则 文件无效的 Windows 驱动器字母主机 验证错误, 将 state 设置为 路径状态

        这是一个(平台无关的)Windows 驱动器字母怪癖。buffer 在此处未重置,而是在 路径状态 中使用。

      2. 否则,如果 buffer 是空字符串,则:

        1. url主机 设置为空字符串。

        2. 如果给出了 state override,则返回。

        3. state 设置为 路径开始状态

      3. 否则,执行以下步骤:

        1. host 成为通过 主机解析 bufferurl 非特殊

        2. 如果 host 失败,则返回失败。

        3. 如果 host 是 "localhost",则将 host 设置为空字符串。

        4. url主机 设置为 host

        5. 如果给出了 state override,则返回。

        6. buffer 设置为空字符串,并将 state 设置为 路径开始状态

    2. 否则,将 c 追加到 buffer

    路径开始状态
    1. 如果 url 是特殊的,则:

      1. 如果 c 是 U+005C (\),无效反斜杠 验证错误

      2. state 设置为 路径状态

      3. 如果 c 既不是 U+002F (/) 也不是 U+005C (\),则将 pointer 减少 1。

    2. 否则,如果未给出 state overridec 是 U+003F (?),则将 url查询 设置为空字符串,并将 state 设置为 查询状态

    3. 否则,如果未给出 state overridec 是 U+0023 (#),则将 url片段 设置为空字符串,并将 state 设置为 片段状态

    4. 否则,如果 c 不是 EOF 代码点

      1. state 设置为 路径状态

      2. 如果 c 不是 U+002F (/) 则将 pointer 减少 1。

    5. 否则,如果给出了 state overrideurl主机 为空,则 追加空字符串到 url路径

    路径状态
    1. 如果以下情况之一为真:

      那么:

      1. 如果 url 是特殊的c 是 U+005C (\),无效反斜杠 验证错误

      2. 如果 buffer双点 URL 路径段,那么:

        1. 缩短 url路径

        2. 如果 c 既不是 U+002F (/), 也不是 url 特殊的c 是 U+005C (\),追加空字符串到 url路径

          这意味着对于输入 /usr/..,结果是 /,而不是没有路径。

      3. 否则,如果 buffer单点 URL 路径段,且 c 既不是 U+002F (/),也不是 url 特殊的c 是 U+005C (\),则 追加空字符串到 url路径

      4. 否则,如果 buffer 不是 单点 URL 路径段,那么:

        1. 如果 url方案 是 "file", url路径 为空,且 bufferWindows 驱动器字母,则将 buffer 的第二个代码点替换为 U+003A (:)。

          这是一个(平台无关的)Windows 驱动器字母怪癖。

        2. 追加 bufferurl路径

      5. buffer 设置为空字符串。

      6. 如果 c 是 U+003F (?),则将 url查询 设置为空字符串,并将 state 设置为 查询状态

      7. 如果 c 是 U+0023 (#),则将 url片段 设置为空字符串,并将 state 设置为 片段状态

    2. 否则,执行以下步骤:

      1. 如果 c 不是 URL 代码点 且不是 U+0025 (%),则 无效 URL 单元 验证错误

      2. 如果 c 是 U+0025 (%) 且 剩余部分 不是以两个 ASCII 十六进制数字 开头,无效 URL 单元 验证错误

      3. UTF-8 百分比编码 c 使用 路径百分比编码集 并将结果追加到 buffer

    不透明路径状态
    1. 如果 c 是 U+003F (?),则将 url查询 设置为空字符串,并将 state 设置为 查询状态

    2. 否则,如果 c 是 U+0023 (#),则将 url片段 设置为空字符串,并将 state 设置为 片段状态

    3. 否则:

      1. 如果 c 不是 EOF 代码点,不是 URL 代码点,也不是 U+0025 (%),则 无效 URL 单元 验证错误

      2. 如果 c 是 U+0025 (%) 且 剩余部分 不是以两个 ASCII 十六进制数字 开头,无效 URL 单元 验证错误

      3. 如果 c 不是 EOF 代码点,则 UTF-8 百分比编码 c 使用 C0 控制百分比编码集 并将结果追加到 url路径

    查询状态
    1. 如果 encoding 不是 UTF-8 且以下情况之一为真:

      则将 encoding 设置为 UTF-8

    2. 如果以下情况之一为真:

      那么:

      1. queryPercentEncodeSet特殊查询百分比编码集 如果 url 是特殊的;否则为 查询百分比编码集

      2. 百分比编码后编码,使用 encodingbufferqueryPercentEncodeSet,并将结果追加到 url查询

        由于有状态的 ISO-2022-JP 编码器,该操作不能逐代码点调用。

      3. buffer 设置为空字符串。

      4. 如果 c 是 U+0023 (#),则将 url片段 设置为空字符串,并将 状态设置为 片段状态

    3. 否则,如果 c 不是 EOF 代码点

      1. 如果 c 不是 URL 代码点 且不是 U+0025 (%),则 无效 URL 单元 验证错误

      2. 如果 c 是 U+0025 (%) 且 剩余部分 不是以两个 ASCII 十六进制数字 开头,则 无效 URL 单元 验证错误

      3. c 追加到 buffer

    片段状态
    1. 如果 c 不是 EOF 代码点,则:

      1. 如果 c 不是 URL 代码点 且不是 U+0025 (%),则 无效 URL 单元 验证错误

      2. 如果 c 是 U+0025 (%) 且 剩余部分 不是以两个 ASCII 十六进制数字 开头,则 无效 URL 单元 验证错误

      3. UTF-8 百分比编码 c,使用 片段百分比编码集,并将结果追加到 url片段

  10. 返回 url


要给定 urlusername 设置用户名,将 url用户名 设置为运行 UTF-8 百分比编码username 上,并使用 用户信息百分比编码集 的结果。

要给定 urlpassword 设置密码,将 url密码 设置为运行 UTF-8 百分比编码password 上,并使用 用户信息百分比编码集 的结果。

4.5. URL 序列化

URL 序列化器 接收一个 URL url,以及一个可选的布尔值 exclude fragment(默认为 false),然后执行以下步骤。它们返回一个 ASCII 字符串

  1. outputurlscheme 和 U+003A (:) 连接后的结果。

  2. 如果 url主机 非空:

    1. 将 "//" 添加到 output

    2. 如果 url 包含凭据,则:

      1. url用户名 添加到 output

      2. 如果 url密码 不是空字符串,则将 U+003A (:) 和 url密码 添加到 output

      3. 将 U+0040 (@) 添加到 output

    3. url主机序列化后的结果,添加到 output

    4. 如果 url端口 非空,则添加 U+003A (:) 和 url端口序列化后的结果,到 output

  3. 如果 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/)。

  4. URL 路径序列化 url 的结果添加到 output

  5. 如果 url查询 非空,则添加 U+003F (?), 后接 url查询,到 output

  6. 如果 exclude fragment 为 false 且 url片段 非空,则添加 U+0023 (#),后接 url片段,到 output

  7. 返回 output

URL 路径序列化器 接收一个 URL url,然后执行以下步骤。它们返回一个 ASCII 字符串

  1. 如果 url 有一个 不透明路径,则返回 url路径

  2. output 为空字符串。

  3. 对每个 segmenturl路径中,添加 U+002F (/) 和 segmentoutput

  4. 返回 output

4.6. URL 等价性

要确定 URL A 是否 等于 URL B,可选的布尔值 排除片段(默认为 false),请执行以下步骤:

  1. serializedA序列化 A 的结果,排除片段 设置为 排除片段

  2. serializedB序列化 B 的结果,排除片段 设置为 排除片段

  3. 如果 serializedA 等于 serializedB,则返回 true;否则返回 false。

4.7.

请参阅 HTML 中的定义,获取必要的背景信息。[HTML]

URL 的源是通过以下步骤,根据 url方案 切换得出的:

"blob"
  1. 如果 urlblob URL 项 非空,则返回 urlblob URL 项环境

  2. pathURL 设为解析 urlURL 路径序列化 结果的结果。

  3. 如果 pathURL 失败,则返回一个新的不透明源

  4. 如果 pathURL方案是 "http"、"https" 或 "file",则返回 pathURL

  5. 返回一个新的不透明源

blob:https://whatwg.org/d0360e2f-caee-469f-9a2f-87d5b0456f6f元组源 ("https"、"whatwg.org"、null、null)。

"ftp"
"http"
"https"
"ws"
"wss"

返回元组源(url方案url主机url端口、null)。

"file"

尽管令人遗憾,这个问题留给读者自行解决。当不确定时,返回一个新的不透明源

其他

返回一个新的不透明源

这确实意味着这些URL无法与自身成为同源

4.8. URL 渲染

当显示 URL 的主要目的是让用户做出安全或信任决定时,应该以其 序列化形式进行渲染,并进行如下所述的修改。例如,用户应根据浏览器地址栏中渲染的 URL 来做出信任决定。

4.8.1. 简化非人类可读或无关的组件

删除可能提供欺骗机会或分散安全相关信息的组件:

4.8.2. 省略

在空间受限的显示中,URL 应小心省略,以避免误导用户做出安全决定:

4.8.3. 国际化和特殊字符

应谨慎处理国际化域名(IDNs)、特殊字符和双向文本,以防止欺骗:

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,然后运行以下步骤:

  1. sequences 设为通过 0x26 (&) 拆分 input 的结果。

  2. output 设为一个最初为空的名称-值元组的 列表,其中名称和值都存储为字符串。

  3. 对每个 字节序列 bytessequences 中:

    1. 如果 bytes 是空字节序列,则 继续

    2. 如果 bytes 包含 0x3D (=),则将 name 设为从 bytes 开头到其第一个 0x3D (=)(不包括)的字节,value 设为(如果有)从第一个 0x3D (=) 之后到 bytes 结尾的字节。如果 0x3D (=) 是第一个字节,则 name 将为空字节序列。如果是最后一个字节,则 value 将为空字节序列。

    3. 否则,将 name 设为 bytes 的值,并将 value 设为空字节序列。

    4. namevalue 中的任何 0x2B (+) 替换为 0x20 (SP)。

    5. nameStringvalueString 设为分别对 namevalue 进行无 BOM 的 UTF-8 解码百分比解码 的结果。

    6. (nameString, valueString) 附加到 output

  4. 返回 output

5.2. application/x-www-form-urlencoded 序列化

application/x-www-form-urlencoded 序列化器 接受一个名称-值元组的列表 tuples,可选地带有一个编码 encoding(默认 UTF-8),然后运行以下步骤。它们返回一个ASCII 字符串

  1. encoding 设置为通过 获取输出编码 获得的结果。

  2. output 设为空字符串。

  3. 对每个 tupletuples 中:

    1. 断言tuple 的名称和 tuple 的值都是 标量值字符串

    2. name 设为运行 百分比编码后编码,使用 encodingtuple 的名称、application/x-www-form-urlencoded 百分比编码集,并且为 true。

    3. value 设为运行 百分比编码后编码,使用 encodingtuple 的值、application/x-www-form-urlencoded 百分比编码集,并且为 true。

    4. 如果 output 不是空字符串,则附加 U+0026 (&) 到 output

    5. name,接着 U+003D (=),接着 value,附加到 output
  4. 返回 output

5.3. 挂钩

application/x-www-form-urlencoded 字符串解析器 接受一个标量值字符串 inputUTF-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

  1. 如果 urlURL 没有 不透明路径,则返回。

  2. 如果 urlURL片段 不为 null,则返回。

  3. 如果 urlURL查询 不为 null,则返回。

  4. urlURL路径 中移除所有尾随的 U+0020 空格 码点

API URL 解析器 接受一个 标量值字符串 url 和一个可选的 null 或 标量值字符串 base(默认为 null),然后运行以下步骤:

  1. parsedBase 为 null。

  2. 如果 base 不为 null:

    1. parsedBase 设置为运行 基本 URL 解析器base 上的结果。

    2. 如果 parsedBase 失败,则返回失败。

  3. 返回运行 基本 URL 解析器urlparsedBase 上的结果。

为了 初始化 一个 URL 对象 url,给定一个 URL urlRecord

  1. queryurlRecord查询,如果它不是 null;否则为空字符串。

  2. urlURL 设置为 urlRecord

  3. url查询对象 设置为一个新的 URLSearchParams 对象。

  4. 初始化 url查询对象 使用 query

  5. url查询对象URL 对象 设置为 url


new URL(url, base) 构造函数的步骤如下:

  1. parsedURL 设置为运行 API URL 解析器urlbase 上的结果,如果有提供 base

  2. 如果 parsedURL 失败,则 抛出一个 TypeError

  3. 初始化 this 使用 parsedURL

为了不使用 基础 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) 的步骤如下:

  1. parsedURL 设置为运行 API URL 解析器urlbase 上的结果,如果有提供 base

  2. 如果 parsedURL 失败,则返回 null。

  3. url 成为一个新的 URL 对象。

  4. 初始化 url,使用 parsedURL

  5. 返回 url

静态方法 canParse(url, base) 的步骤如下:

  1. parsedURL 设置为运行 API URL 解析器urlbase 上的结果,如果有提供 base

  2. 如果 parsedURL 失败,则返回 false。

  3. 返回 true。


步骤 href 获取器 和 toJSON() 方法的步骤是返回 序列化 thisURL

步骤 href 设置器如下:

  1. parsedURL 成为在给定值上运行 基本 URL 解析器 的结果。

  2. 如果 parsedURL 失败,则 抛出一个 TypeError

  3. 设置 thisURLparsedURL

  4. 清空 this查询对象列表

  5. query 成为 thisURL查询

  6. 如果 query 非空,则将 this查询对象列表 设置为 解析 query 的结果。

步骤 origin 获取器 是返回 序列化 thisURL来源[HTML]

步骤 protocol 获取器 是返回 thisURL协议,后跟 U+003A (:)。

步骤 protocol 设置器 是使用 基本 URL 解析 进行设置,给定值后跟 U+003A (:),并将 thisURL 作为 url,以及 scheme start state 作为 状态覆盖

步骤 username 获取器是返回 thisURL用户名

步骤 username 设置器如下:

  1. 如果 thisURL 不能有用户名/密码/端口,则返回。

  2. 给定 thisURL 和给定的值,设置用户名

步骤 password 获取器是返回 thisURL密码

步骤 password 设置器如下:

  1. 如果 thisURL 不能有用户名/密码/端口,则返回。

  2. 给定 thisURL 和给定的值,设置密码

步骤 host 获取器如下:

  1. url 成为 thisURL

  2. 如果 url主机 为 null,则返回空字符串。

  3. 如果 url端口 为 null,则返回 url主机序列化后的结果。

  4. 返回 url主机序列化后的结果,后跟 U+003A (:) 和 url端口序列化后的结果。

步骤 host 设置器如下:

  1. 如果 thisURL不透明路径,则返回。

  2. 使用 基本 URL 解析器 对给定值进行解析,传递 thisURL 作为 url主机状态 作为 状态覆盖

如果设置器的给定值没有 端口,那么 thisURL端口 将不会改变。这可能会让人感到意外,因为 host 获取器返回 URL-端口字符串,因此可能有人认为设置器会始终“重置”两者。

步骤 hostname 获取器如下:

  1. 如果 thisURL主机 为 null,则返回空字符串。

  2. 返回 thisURL主机序列化后的结果。

步骤 hostname 设置器如下:

  1. 如果 thisURL不透明路径,则返回。

  2. 使用 基本 URL 解析器 对给定值进行解析,传递 thisURL 作为 url主机名状态 作为 状态覆盖

步骤 port 获取器如下:

  1. 如果 thisURL端口 为 null,则返回空字符串。

  2. 返回 thisURL端口序列化后的结果。

步骤 port 设置器如下:

  1. 如果 thisURL 不能有用户名/密码/端口,则返回。

  2. 如果给定的值是空字符串,则将 thisURL端口 设置为 null。

  3. 否则,使用 基本 URL 解析器 对给定值进行解析,传递 thisURL 作为 url端口状态 作为 状态覆盖

步骤 pathname 获取器是返回URL 路径序列化的结果 thisURL

步骤 pathname 设置器如下:

  1. 如果 thisURL不透明路径,则返回。

  2. 清空 thisURL路径

  3. 使用 基本 URL 解析器 对给定值进行解析,传递 thisURL 作为 url路径启动状态 作为 状态覆盖

步骤 search 获取器如下:

  1. 如果 thisURL查询 是 null 或者为空字符串,则返回空字符串。

  2. 返回 U+003F (?),后跟 thisURL查询

步骤 search 设置器如下:

  1. url 成为 thisURL

  2. 如果给定值是空字符串:

    1. url查询 设置为 null。

    2. 清空 this查询对象列表

    3. 使用 可能从不透明路径中去除尾随空格 来处理 this

    4. 返回。

  3. 将给定值中的单个前导 U+003F (?) 移除(如果存在),然后让 input 成为结果。

  4. url查询 设置为空字符串。

  5. 使用 基本 URL 解析器input 进行解析,传递 url 作为 url查询状态 作为 状态覆盖

  6. this查询对象列表 设置为 解析 input 的结果。

search 设置器可能会从 thisURL路径 中移除尾随 U+0020 空格 码位。这是为了确保对 URL 序列化器 输出运行 URL 解析器 后不会得到一个与 URL相等 的结果。

步骤 searchParams 获取器如下:返回 this查询对象

步骤 hash 获取器如下:

  1. 如果 thisURL片段 是 null 或者为空字符串,则返回空字符串。

  2. 返回 U+0023 (#),后跟 thisURL片段

步骤 hash 设置器如下:

  1. 如果给定值是空字符串:

    1. thisURL片段 设置为 null。

    2. 使用 可能从不透明路径中去除尾随空格 来处理 this

    3. 返回。

  2. 将给定值中的单个前导 U+0023 (#) 移除(如果存在),然后让 input 成为结果。

  3. thisURL片段 设置为空字符串。

  4. 使用 基本 URL 解析器input 进行解析,传递 thisURL 作为 url片段状态 作为 状态覆盖

hash 设置器可能会像 search 设置器一样更改 thisURL路径

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 对象(包括 hrefsearch)有所不同。当使用 searchParams 操作一个 URLquery 时,这尤其容易让人感到意外。

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 setspecial-query percent-encode set 中的字符进行百分比编码(取决于 URL 是否 特殊)。

一个 URLSearchParams 对象具有一个关联的:

一个具有非 null URL 对象URLSearchParams 对象有可能以与 URL 对象的 searchhash setter 等效的方式更改该对象的 路径

初始化 一个 URLSearchParams 对象 query 使用 init:

  1. 如果 init 是一个 序列,则 对于每个 innerSequenceinit 中:

    1. 如果 innerSequence大小 不是 2,则 抛出 一个 TypeError

    2. 追加 (innerSequence[0], innerSequence[1]) 到 query列表

  2. 否则,如果 init 是一个 记录,则 对于每个 namevalueinit 中,追加 (name, value) 到 query列表

  3. 否则:

    1. 断言:init 是一个字符串。

    2. query列表 设置为 解析 init 的结果。

更新 一个 URLSearchParams 对象 query

  1. 如果 queryURL 对象 为 null,则返回。

  2. serializedQuery序列化 query列表 的结果。

  3. 如果 serializedQuery 是空字符串,则将 serializedQuery 设置为 null。

  4. queryURL 对象URL查询 设置为 serializedQuery

  5. 如果 serializedQuery 是 null,则 可能去除不透明路径中的尾随空格,并使用 queryURL 对象

new URLSearchParams(init) 构造函数步骤是:

  1. 如果 init 是字符串并且以 U+003F (?) 开始,则删除 init 的第一个码点。

  2. 初始化 this 使用 init

size 获取步骤是返回 this列表大小

append(name, value) 方法步骤是:

  1. 追加 (name, value) 到 this列表

  2. 更新 this

delete(name, value) 方法步骤是:

  1. 如果给定 value,则删除所有 元组,其名称是 name,且值是 value, 从 this列表中。

  2. 否则,删除所有 元组,其名称是 name,从 this列表中。

  3. 更新 this

get(name) 方法步骤是返回 第一个名称为 namethis列表中的值, 如果有此类元组;否则返回 null。

getAll(name) 方法步骤是返回 所有名称为 namethis列表中的值, 按列表顺序;否则返回空序列。

has(name, value) 方法步骤是:

  1. 如果给定 value 并且存在一个 元组, 其名称为 name,值为 valuethis列表中,则返回 true。

  2. 如果未给定 value 并且存在一个 元组, 其名称为 namethis列表中,则返回 true。

  3. 返回 false。

set(name, value) 方法步骤是:

  1. 如果 this列表 包含 任何 元组,其名称为 name,则将第一个此类 元组 的值设置为 value删除其他。

  2. 否则,追加 (name, value) 到 this列表

  3. 更新 this


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()

sort() 方法步骤是:

  1. this列表 中的所有 元组 按其名称进行排序。排序必须按代码单元比较进行。对于具有相同名称的元组,其相对顺序必须保持不变。

  2. 更新 this


要迭代的值对this列表元组,键为名称,值为值。

字符串化行为 步骤是返回 this列表序列化

6.3. 其他标准中的 URL API

一个公开 URL 的标准,应当将 URL 作为字符串公开(通过 序列化 内部的 URL)。标准不应当通过 URL 使用 URL 对象。 URL 对象是用于 URL 操作的。在 IDL 中应当使用 USVString 类型。

这里的高层次概念是值应当作为不可变的数据结构公开。

如果某标准决定使用 "URL" 名称的变体来定义其功能,建议将此功能命名为 "url"(即小写,并且以 "l" 结尾)。不应使用 "URL"、"URI" 和 "IRI" 等名称。然而,如果名称是复合词,建议使用 "URL"(即大写),例如 "newURL" 和 "oldURL"。

接口 EventSourceHashChangeEventHTML 中正确命名的示例。 [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 编写(Appleannevk@annevk.nl)。

知识产权

版权 © WHATWG (Apple, Google, Mozilla, Microsoft)。此作品依据 知识共享署名 4.0 国际许可协议 授权。若本作品的部分内容被纳入源代码,则源代码部分依据 BSD 3-Clause 许可协议 授权。

这是现行标准。对专利审核版感兴趣的用户可以查看 现行标准审核草案

索引

本规范定义的术语

引用定义的术语

参考文献

规范性引用

[BIDI]
Manish Goregaokar मनीष गोरेगांवकर; Robin Leroy. Unicode 双向算法。 2023年8月15日。Unicode 标准附录 #9。URL: https://www.unicode.org/reports/tr9/tr9-48.html
[ENCODING]
Anne van Kesteren. 编码标准。现行标准。URL: https://encoding.spec.whatwg.org/
[FILEAPI]
Marijn Kruisselbrink. 文件 API。URL: https://w3c.github.io/FileAPI/
[HTML]
Anne van Kesteren 等人。HTML 标准。现行标准。URL: https://html.spec.whatwg.org/multipage/
[IANA-URI-SCHEMES]
统一资源标识符 (URI) 方案。URL: https://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml
[INFRA]
Anne van Kesteren; Domenic Denicola. 基础结构标准。现行标准。URL: https://infra.spec.whatwg.org/
[PSL]
公共后缀列表。Mozilla 基金会。
[RFC4291]
R. Hinden; S. Deering. IPv6 地址架构。2006年2月。草案标准。URL: https://www.rfc-editor.org/rfc/rfc4291
[UTS46]
Mark Davis; Michel Suignard. Unicode IDNA 兼容性处理。2023年9月5日。Unicode 技术标准 #46。URL: https://www.unicode.org/reports/tr46/tr46-31.html
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL 标准。现行标准。URL: https://webidl.spec.whatwg.org/

非规范性引用

[ECMA-262]
ECMAScript 语言规范。URL: https://tc39.es/ecma262/multipage/
[IDNFAQ]
国际化域名 (IDN) 常见问题。URL: https://unicode.org/faq/idn.html
[RFC1034]
P. Mockapetris. 域名 - 概念和设施。1987年11月。互联网标准。URL: https://www.rfc-editor.org/rfc/rfc1034
[RFC3986]
T. Berners-Lee; R. Fielding; L. Masinter. 统一资源标识符 (URI):通用语法。2005年1月。互联网标准。URL: https://www.rfc-editor.org/rfc/rfc3986
[RFC3987]
M. Duerst; M. Suignard. 国际化资源标识符 (IRIs)。2005年1月。建议标准。URL: https://www.rfc-editor.org/rfc/rfc3987
[RFC5890]
J. Klensin. 应用程序国际化域名 (IDNA):定义与文档框架。2010年8月。建议标准。URL: https://www.rfc-editor.org/rfc/rfc5890
[RFC5952]
S. Kawamura; M. Kawashima. IPv6 地址文本表示的建议。2010年8月。建议标准。URL: https://www.rfc-editor.org/rfc/rfc5952
[RFC6454]
A. Barth. Web 源概念。2011年12月。建议标准。URL: https://www.rfc-editor.org/rfc/rfc6454
[RFC7595]
D. Thaler, Ed.; T. Hansen; T. Hardie. URI 方案的指南和注册程序。2015年6月。最佳当前实践。URL: https://www.rfc-editor.org/rfc/rfc7595
[RFC791]
J. Postel. 互联网协议。1981年9月。互联网标准。URL: https://www.rfc-editor.org/rfc/rfc791
[UTR36]
Mark Davis; Michel Suignard. Unicode 安全注意事项。2014年9月19日。Unicode 技术报告 #36。URL: https://www.unicode.org/reports/tr36/tr36-15.html
[UTS39]
Mark Davis; Michel Suignard. Unicode 安全机制。2023年9月5日。Unicode 技术标准 #39。URL: https://www.unicode.org/reports/tr39/tr39-28.html

IDL 索引

[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();
};

[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;
};

MDN

URL/URL

In all current engines.

Firefox26+Safari14.1+Chrome19+
Opera?Edge79+
Edge (Legacy)12+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
Node.js10.0.0+
MDN

URL/canParse_static

Firefox115+Safari17+ChromeNone
Opera?EdgeNone
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
Node.js20.0.0+
MDN

URL/hash

In all current engines.

Firefox22+Safari7+Chrome32+
Opera?Edge79+
Edge (Legacy)13+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
Node.js7.0.0+
MDN

URL/host

In all current engines.

Firefox22+Safari7+Chrome32+
Opera?Edge79+
Edge (Legacy)13+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
Node.js7.0.0+
MDN

URL/hostname

In all current engines.

Firefox22+Safari10+Chrome32+
Opera?Edge79+
Edge (Legacy)13+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
Node.js7.0.0+
MDN

URL/href

In all current engines.

Firefox22+Safari10+Chrome32+
Opera?Edge79+
Edge (Legacy)13+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
Node.js7.0.0+
MDN

URL/origin

In all current engines.

Firefox26+Safari10+Chrome32+
Opera?Edge79+
Edge (Legacy)12+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet6.0+Opera Mobile?
Node.js7.0.0+
MDN

URL/password

In all current engines.

Firefox26+Safari10+Chrome32+
Opera?Edge79+
Edge (Legacy)12+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet6.0+Opera Mobile?
Node.js7.0.0+
MDN

URL/pathname

In all current engines.

Firefox22+Safari10+Chrome32+
Opera?Edge79+
Edge (Legacy)13+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
Node.js7.0.0+
MDN

URL/port

In all current engines.

Firefox22+Safari10+Chrome32+
Opera?Edge79+
Edge (Legacy)13+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
Node.js7.0.0+
MDN

URL/protocol

In all current engines.

Firefox22+Safari10+Chrome32+
Opera?Edge79+
Edge (Legacy)13+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
Node.js7.0.0+
MDN

URL/search

In all current engines.

Firefox22+Safari10+Chrome32+
Opera?Edge79+
Edge (Legacy)13+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
Node.js7.0.0+
MDN

URL/searchParams

In all current engines.

Firefox29+Safari10.1+Chrome51+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
Node.js7.5.0+
MDN

URL/toJSON

In all current engines.

Firefox54+Safari11+Chrome71+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
Node.js7.7.0+
MDN

URL/toString

In all current engines.

Firefox54+Safari7+Chrome19+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet6.0+Opera Mobile?
Node.js7.0.0+
MDN

URL/username

In all current engines.

Firefox26+Safari10+Chrome32+
Opera?Edge79+
Edge (Legacy)12+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet6.0+Opera Mobile?
Node.js7.0.0+
MDN

URL

In all current engines.

Firefox19+Safari7+Chrome32+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android?iOS Safari?Chrome for Android?Android WebView4.4+Samsung Internet?Opera Mobile?
Node.js10.0.0+
MDN

URLSearchParams/URLSearchParams

In all current engines.

Firefox29+Safari10.1+Chrome49+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
Node.js7.5.0+

URLSearchParams/entries

In all current engines.

Firefox44+Safari10.1+Chrome49+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
Node.js7.5.0+

URLSearchParams/forEach

In all current engines.

Firefox44+Safari10.1+Chrome49+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
Node.js7.5.0+

URLSearchParams/keys

In all current engines.

Firefox44+Safari10.1+Chrome49+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
Node.js7.5.0+

URLSearchParams/values

In all current engines.

Firefox44+Safari10.1+Chrome49+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
Node.js7.5.0+
MDN

URLSearchParams/append

In all current engines.

Firefox29+Safari10.1+Chrome49+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
Node.js7.5.0+
MDN

URLSearchParams/delete

In all current engines.

Firefox29+Safari14+Chrome49+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
Node.js7.5.0+
MDN

URLSearchParams/get

In all current engines.

Firefox29+Safari10.1+Chrome49+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
Node.js7.5.0+
MDN

URLSearchParams/getAll

In all current engines.

Firefox29+Safari10.1+Chrome49+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
Node.js7.5.0+
MDN

URLSearchParams/has

In all current engines.

Firefox29+Safari10.1+Chrome49+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
Node.js7.5.0+
MDN

URLSearchParams/set

In all current engines.

Firefox29+Safari10.1+Chrome49+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
Node.js7.5.0+
MDN

URLSearchParams/size

In all current engines.

Firefox112+Safari17+Chrome113+
Opera?Edge113+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
Node.js19.0.0+
MDN

URLSearchParams/sort

In all current engines.

Firefox54+Safari11+Chrome61+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
Node.js7.7.0+
MDN

URLSearchParams/toString

In all current engines.

Firefox29+Safari10.1+Chrome49+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
Node.js7.5.0+
MDN

URLSearchParams

In all current engines.

Firefox29+Safari10.1+Chrome49+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
Node.js10.0.0+