网络工作组 T. Berners-Lee
征求意见稿:3986 W3C/MIT
STD:66 R. Fielding
更新:1738 Day Software
废止:2732、2396、1808 L. Masinter
类别:标准轨道 Adobe Systems
2005 年 1 月
统一资源标识符(URI):通用语法
本备忘录状态
本文档为 Internet 社区规定了一个 Internet 标准轨道协议,
并请求对其改进提出讨论和建议。请参阅当前版本的
“Internet Official Protocol Standards”(STD 1),以了解
本协议的标准化阶段和状态。本备忘录的分发不受限制。
Copyright Notice
Copyright (C) The Internet Society (2005).
摘要
统一资源标识符(URI)是一个紧凑的字符序列,用于标识
抽象或物理资源。本规范定义了通用 URI 语法,以及一种解析
可能采用相对形式的 URI 引用的过程,同时还给出了在
Internet 上使用 URI 的指南和安全考虑。URI 语法定义了一种
语法,它是所有有效 URI 的超集,使实现能够在不了解每一种
可能标识符的特定于方案的要求的情况下,解析 URI 引用的
公共组成部分。本规范并不为 URI 定义生成式语法;该任务由
各个 URI 方案的具体规范完成。
Berners-Lee, et al. 标准轨道 [第 1 页]
RFC 3986 URI 通用语法 2005 年 1 月
目录
1. 引言 . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.1. URI 概述 . . . . . . . . . . . . . . . . . . . . . 4
1.1.1. 通用语法 . . . . . . . . . . . . . . . . . . 6
1.1.2. 示例 . . . . . . . . . . . . . . . . . . . . 7
1.1.3. URI、URL 和 URN . . . . . . . . . . . . . . 7
1.2. 设计考虑 . . . . . . . . . . . . . . . . . . . . 8
1.2.1. 转录 . . . . . . . . . . . . . . . . . . . 8
1.2.2. 将标识与交互分离 . . . . . . . . . . . . . 9
1.2.3. 层次化标识符 . . . . . . . . . . . . . . . 10
1.3. 语法表示法 . . . . . . . . . . . . . . . . . . . 11
2. 字符 . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.1. 百分号编码 . . . . . . . . . . . . . . . . . . . . 12
2.2. 保留字符 . . . . . . . . . . . . . . . . . . . . 12
2.3. 非保留字符 . . . . . . . . . . . . . . . . . . . 13
2.4. 何时编码或解码 . . . . . . . . . . . . . . . . . 14
2.5. 标识数据 . . . . . . . . . . . . . . . . . . . . 14
3. 语法组成部分 . . . . . . . . . . . . . . . . . . . . . 16
3.1. 方案 . . . . . . . . . . . . . . . . . . . . . . . 17
3.2. 授权机构 . . . . . . . . . . . . . . . . . . . . 17
3.2.1. 用户信息 . . . . . . . . . . . . . . . . . . 18
3.2.2. 主机 . . . . . . . . . . . . . . . . . . . . 18
3.2.3. 端口 . . . . . . . . . . . . . . . . . . . . 22
3.3. 路径 . . . . . . . . . . . . . . . . . . . . . . . 22
3.4. 查询 . . . . . . . . . . . . . . . . . . . . . . 23
3.5. 片段 . . . . . . . . . . . . . . . . . . . . . . 24
4. 用法 . . . . . . . . . . . . . . . . . . . . . . . . . 25
4.1. URI 引用 . . . . . . . . . . . . . . . . . . . . 25
4.2. 相对引用 . . . . . . . . . . . . . . . . . . . . 26
4.3. 绝对 URI . . . . . . . . . . . . . . . . . . . . 27
4.4. 同文档引用 . . . . . . . . . . . . . . . . . . . 27
4.5. 后缀引用 . . . . . . . . . . . . . . . . . . . . 27
5. 引用解析 . . . . . . . . . . . . . . . . . . . . . . . . 28
5.1. 建立基准 URI . . . . . . . . . . . . . . . . . . 28
5.1.1. 嵌入内容中的基准 URI . . . . . . . . . . . . 29
5.1.2. 来自封装实体的基准 URI . . . . . . . . . . 29
5.1.3. 来自检索 URI 的基准 URI . . . . . . . . . 30
5.1.4. 默认基准 URI . . . . . . . . . . . . . . . . 30
5.2. 相对解析 . . . . . . . . . . . . . . . . . . . . 30
5.2.1. 预解析基准 URI . . . . . . . . . . . . . . . 31
5.2.2. 转换引用 . . . . . . . . . . . . . . . . . . 31
5.2.3. 合并路径 . . . . . . . . . . . . . . . . . 32
5.2.4. 移除点段 . . . . . . . . . . . . . . . . . 33
5.3. 组成部分重组 . . . . . . . . . . . . . . . . . . 35
5.4. 引用解析示例 . . . . . . . . . . . . . . . . . . 35
5.4.1. 正常示例 . . . . . . . . . . . . . . . . . 36
5.4.2. 异常示例 . . . . . . . . . . . . . . . . . 36
Berners-Lee, et al. 标准轨道 [第 2 页]
RFC 3986 URI 通用语法 2005 年 1 月
6. 规范化与比较 . . . . . . . . . . . . . . . . . . . . . . . 38
6.1. 等价性 . . . . . . . . . . . . . . . . . . . . . 38
6.2. 比较阶梯 . . . . . . . . . . . . . . . . . . . . 39
6.2.1. 简单字符串比较 . . . . . . . . . . . . . . . 39
6.2.2. 基于语法的规范化 . . . . . . . . . . . . . . 40
6.2.3. 基于方案的规范化 . . . . . . . . . . . . . . 41
6.2.4. 基于协议的规范化 . . . . . . . . . . . . . . 42
7. 安全考虑 . . . . . . . . . . . . . . . . . . . . . . . 43
7.1. 可靠性与一致性 . . . . . . . . . . . . . . . . . 43
7.2. 恶意构造 . . . . . . . . . . . . . . . . . . . . 43
7.3. 后端转码 . . . . . . . . . . . . . . . . . . . . 44
7.4. 罕见 IP 地址格式 . . . . . . . . . . . . . . . . 45
7.5. 敏感信息 . . . . . . . . . . . . . . . . . . . . 45
7.6. 语义攻击 . . . . . . . . . . . . . . . . . . . . 45
8. IANA 考虑事项 . . . . . . . . . . . . . . . . . . . . . 46
9. 致谢 . . . . . . . . . . . . . . . . . . . . . . . . . . 46
10. 参考文献 . . . . . . . . . . . . . . . . . . . . . . . . 46
10.1. 规范性参考文献 . . . . . . . . . . . . . . . . . . 46
10.2. 资料性参考文献 . . . . . . . . . . . . . . . . . . 47
A. URI 的汇总 ABNF . . . . . . . . . . . . . . . . . . . . . 49
B. 使用正则表达式解析 URI 引用 . . . . . . . . . . . . . . 50
C. 在上下文中界定 URI . . . . . . . . . . . . . . . . . . . 51
D. 相对于 RFC 2396 的变更 . . . . . . . . . . . . . . . . 53
D.1. 增补 . . . . . . . . . . . . . . . . . . . . . . 53
D.2. 修改 . . . . . . . . . . . . . . . . . . . . . . 53
索引 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
作者地址 . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
完整版权声明 . . . . . . . . . . . . . . . . . . . . . . . . . 61
Berners-Lee, et al. 标准轨道 [第 3 页]
RFC 3986 URI 通用语法 2005 年 1 月
1. 引言
统一资源标识符(URI)提供了一种简单且可扩展的方式来标识
资源。本 URI 语法和语义规范源自万维网全球信息倡议所引入
的概念;该倡议自 1990 年起使用这些标识符,并在
“Universal Resource Identifiers in WWW” [RFC1630] 中有所描述。
该语法被设计为满足“Functional Recommendations for Internet
Resource Locators” [RFC1736] 和“Functional Requirements for Uniform
Resource Names” [RFC1737] 中提出的建议。
本文档废止 [RFC2396],后者合并了“Uniform Resource
Locators” [RFC1738] 和“Relative Uniform Resource Locators”
[RFC1808],以便为所有 URI 定义单一的通用语法。
它还废止了 [RFC2732],后者引入了 IPv6 地址语法。
它排除了 RFC 1738 中定义各个 URI 方案特定语法的部分;
这些部分将作为单独文档更新。新 URI 方案的注册过程由
[BCP35] 单独定义。新 URI 方案设计者可在 [RFC2718] 中
找到相关建议。相对于 RFC 2396 的所有重要变更均记录在
附录 D 中。
本规范按照 [BCP19] 给出的定义使用术语“字符”和“编码字符
集”,并使用“字符编码”来代替 [BCP19] 所称的
“charset”。
1.1. URI 概述
URI 具有如下特征:
统一
统一性提供了若干好处。它允许不同类型的资源标识符在
同一上下文中使用,即使访问这些资源所使用的机制可能
不同。它允许跨不同类型的资源标识符,对公共语法约定
进行统一的语义解释。它允许引入新的资源标识符类型,
而不会干扰现有标识符的使用方式。它允许标识符在许多
不同上下文中复用,从而使新的应用或协议能够利用一个
预先存在的、大型且广泛使用的资源标识符集合。
Berners-Lee, et al. 标准轨道 [第 4 页]
RFC 3986 URI 通用语法 2005 年 1 月
资源
本规范不限制什么可以成为资源的范围;相反,术语
“资源”以一般意义使用,指任何可由 URI 标识的事物。
常见示例包括电子文档、图像、具有一致用途的信息来源
(例如,“洛杉矶今天的天气报告”)、服务(例如,
HTTP 到 SMS 网关)以及其他资源的集合。资源并不一定
可通过 Internet 访问;例如,人、公司以及图书馆中的
装订书籍也可以是资源。同样,抽象概念也可以是资源,
例如数学方程中的运算符和操作数、关系的类型(例如,
“父母”或“雇员”),或数值(例如,零、一和无穷)。
标识符
标识符体现了在其标识范围内,将被标识对象与所有其他
事物区分开来所需的信息。我们对术语“标识”和
“进行标识”的使用,指的是这种将一个资源与所有其他
资源区分开来的目的,而不论该目的如何实现(例如,
通过名称、地址或上下文)。不应误以为这些术语假定
标识符定义或体现了被引用对象的身份,尽管对某些
标识符而言可能如此。也不应假定使用 URI 的系统会访问
被标识的资源:在许多情况下,URI 用于表示资源,而无意
访问它们。同样,被标识的“一个”资源在性质上也可能并非
单一的(例如,某个资源可能是一个命名集合,或一个会
随时间变化的映射)。
URI 是由一串字符组成的标识符,并匹配 第 3 节 中名为
<URI> 的语法规则。它通过一组单独定义的、可扩展的命名
方案(第 3.1 节)实现对资源的统一标识。如何完成、分配或
启用这种标识,则委托给各个方案规范。
本规范不对资源的性质、应用可能寻求引用资源的原因,或为
标识资源而可能使用 URI 的系统种类施加任何限制。本规范
不要求 URI 随时间持续标识同一个资源,尽管这是所有 URI
方案的共同目标。尽管如此,本规范中的任何内容都不妨碍
Berners-Lee, et al. 标准轨道 [第 5 页]
RFC 3986 URI 通用语法 2005 年 1 月
应用将自身限制为特定类型的资源,或限制为保持该应用所
期望特性的某个 URI 子集。
URI 具有全局范围,并且无论上下文如何都会被一致地解释,
尽管解释结果可能与最终用户的上下文相关。例如,
“http://localhost/” 对该引用的每个用户都有相同解释,
即使与 “localhost” 对应的网络接口可能因最终用户而异:
解释独立于访问。然而,基于该引用而采取的操作会相对于
最终用户的上下文发生,这意味着,若某项操作意在引用一个
全局唯一的事物,则必须使用能够将该资源与所有其他事物
区分开的 URI。相对于最终用户本地上下文进行标识的 URI,
只应在该上下文本身是资源定义方面时使用,例如,在线帮助
手册引用最终用户文件系统上的某个文件(例如,
“file:///etc/hosts”)。
1.1.1. 通用语法
每个 URI 都以一个方案名开始,如 第 3.1 节 所定义,该方案名
指向一份用于在该方案内分配标识符的规范。因此,URI 语法
是一种联合且可扩展的命名系统,其中每个方案的规范都可以
进一步限制使用该方案的标识符的语法和语义。
本规范定义了所有 URI 方案都需要或许多 URI 方案共有的
URI 语法元素。因此,它定义了实现独立于方案的 URI 引用
解析机制所需的语法和语义;通过这种机制,对 URI 的依赖
于方案的处理可以推迟到需要依赖于方案的语义时再进行。
同样,使用 URI 引用的协议和数据格式可以引用本规范,作为
对所有 URI 允许语法范围的定义,包括那些尚未定义的方案。
这将标识方案的演化与使用 URI 的协议、数据格式和实现的
演化解耦。
通用 URI 语法的解析器可以把任何 URI 引用解析为其主要
组成部分。一旦确定了方案,便可以对这些组成部分执行进一
步的特定于方案的解析。换句话说,URI 通用语法是所有 URI
方案语法的超集。
Berners-Lee, et al. 标准轨道 [第 6 页]
RFC 3986 URI 通用语法 2005 年 1 月
1.1.2. 示例
以下 URI 示例展示了若干 URI 方案及其公共语法组成部分的
变化:
ftp://ftp.is.co.za/rfc/rfc1808.txt
http://www.ietf.org/rfc/rfc2396.txt
ldap://[2001:db8::7]/c=GB?objectClass?one
mailto:John.Doe@example.com
news:comp.infosystems.www.servers.unix
tel:+1-816-555-1212
telnet://192.0.2.16:80/
urn:oasis:names:specification:docbook:dtd:xml:4.1.2
1.1.3. URI、URL 和 URN
URI 可以进一步分类为定位符、名称,或两者兼有。术语
“统一资源定位符”(URL)指 URI 的一个子集,它除了标识资源
之外,还通过描述其主要访问机制(例如,其网络“位置”)
提供一种定位该资源的方式。术语“统一资源名称”(URN)在
历史上既用于指 “urn” 方案下的 URI [RFC2141],这类 URI 被要求
即使资源不复存在或不可用也仍保持全局唯一且持久,也用于
指任何其他具有名称属性的 URI。
单个方案不必被归类为仅是“名称”或“定位符”之一。来自任意
给定方案的 URI 实例可能具有名称或定位符的特征,或两者
兼有,这通常取决于命名机构分配标识符时的持久性和谨慎
程度,而不是取决于方案本身的任何性质。未来的规范和相关
文档应使用一般术语“URI”,而不是限制性更强的术语“URL”和
“URN” [RFC3305]。
Berners-Lee, et al. 标准轨道 [第 7 页]
RFC 3986 URI 通用语法 2005 年 1 月
1.2. 设计考虑
1.2.1. 转录
URI 语法在设计时将全球范围内的转录作为主要考虑之一。
URI 是来自非常有限集合的一串字符:基本拉丁字母的字母、
数字,以及少量特殊字符。URI 可以用多种方式表示;例如,
纸上的墨迹、屏幕上的像素,或字符编码八位组序列。URI 的
解释只取决于所使用的字符,而不取决于这些字符在网络协议
中如何表示。
转录的目标可以用一个简单场景来描述。设想两位同事 Sam 和
Kim 坐在一次国际会议的酒吧里交流研究想法。Sam 向 Kim
询问获取更多信息的位置,于是 Kim 把该研究站点的 URI 写
在餐巾纸上。回到家后,Sam 拿出餐巾纸,把 URI 输入计算机,
计算机随后检索 Kim 所指的信息。
该场景揭示了若干设计考虑:
o URI 是一串字符,并不总是表示为八位组序列。
o URI 可能从非网络来源转录而来,因此应由最可能在各种
语言和区域设置下,受键盘(及相关输入设备)约束时仍能
输入到计算机中的字符组成。
o URI 经常需要被人记住,而当 URI 由有意义或熟悉的组成
部分构成时,人们更容易记住它。
这些设计考虑并不总是一致。例如,URI 组成部分最有意义的
名称往往需要某些系统无法输入的字符。将资源标识符从一种
媒介转录到另一种媒介的能力,被认为比让 URI 由最有意义的
组成部分构成更重要。
在本地或区域上下文中,随着技术进步,用户可能会受益于
能够使用更大范围的字符;这种使用不由本规范定义。如果
方案或引用 URI 的协议元素允许这种表示,则可以在 URI 中
使用百分号编码的八位组(第 2.1 节)来表示 US-ASCII 编码字符集
Berners-Lee, et al. 标准轨道 [第 8 页]
RFC 3986 URI 通用语法 2005 年 1 月
范围之外的字符。这样的定义应指定在这些字符被百分号编码为
URI 之前,用于将这些字符映射到八位组的字符编码。
1.2.2. 将标识与交互分离
关于 URI 的一个常见误解是:它们只用于引用可访问的资源。
URI 本身只提供标识;资源的访问既不因 URI 的存在而得到
保证,也不由此暗示。相反,与 URI 引用关联的任何操作,
都由该引用出现于其中的协议元素、数据格式属性或自然语言
文本来定义。
给定一个 URI,系统可以尝试对资源执行各种操作,这些操作
可由“访问”、“更新”、“替换”或“查找属性”等词来表征。这些
操作由使用 URI 的协议定义,而不是由本规范定义。不过,
我们确实使用少量通用术语来描述对 URI 的常见操作。URI
“解析”是确定访问机制以及解引用 URI 所需适当参数的过程;
这种解析可能需要若干次迭代。使用该访问机制对 URI 的资源
执行操作,就是“解引用”该 URI。
当 URI 在信息检索系统中用于标识信息来源时,最常见的 URI
解引用形式是“检索”:使用 URI 来检索其关联资源的表示。
“表示”是一串八位组,以及描述这些八位组的表示元数据,
它构成资源在该表示生成时状态的记录。检索通过一个过程
实现,该过程可能包括:使用 URI 作为缓存键来检查本地缓存
的表示;解析 URI 以确定合适的访问机制(如果有);以及
为应用检索操作而解引用 URI。根据用于执行检索的协议,可能
还会提供关于资源(资源元数据)及其与其他资源关系的附加
信息。
信息检索系统中的 URI 引用被设计为后期绑定:访问结果通常
在访问时确定,并且可能随时间或交互的其他方面而变化。这些
引用是为了将来使用而创建的:被标识的不是过去获得的某个
特定结果,而是预计未来结果会满足的某种特征。在这种情况
下,URI 所引用的资源实际上是一种随时间观察到的特征相同性,
Berners-Lee, et al. 标准轨道 [第 9 页]
RFC 3986 URI 通用语法 2005 年 1 月
也许还由资源提供者作出的附加评论或断言加以阐明。
尽管许多 URI 方案以协议命名,但这并不意味着使用这些 URI
将导致通过所命名的协议访问资源。URI 常常只是为了标识而
使用。即使 URI 用于检索资源的表示,该访问也可能通过网关、
代理、缓存和名称解析服务进行,而这些服务独立于与方案名
关联的协议。某些 URI 的解析可能需要使用一个以上的协议
(例如,当本地缓存中找不到表示时,访问 “http” URI 的源
服务器通常同时使用 DNS 和 HTTP)。
1.2.3. 层次化标识符
URI 语法按层次组织,其组成部分按重要性从高到低、从左到右
列出。对于某些 URI 方案,可见的层次结构仅限于方案本身:
方案组成部分定界符(“:”)之后的一切都被认为对 URI 处理
来说是不透明的。其他 URI 方案则使层次结构显式可见,以便
通用解析算法处理。
通用语法使用斜杠(“/”)、问号(“?”)和数字符号(“#”)
字符来界定对通用解析器的标识符层次化解释有意义的组成
部分。除了通过一致使用熟悉的语法来帮助提高这类标识符的
可读性之外,这种跨命名方案的统一层次表示,还允许不依赖
方案的引用相对于该层次结构进行。
通常会有一组或一棵文档“树”被构建来服务某个共同目的,
其中这些文档中的绝大多数 URI 引用都指向该树内部的资源,
而不是外部资源。类似地,位于某个特定站点的文档引用同一
站点中其他资源的可能性,远高于引用远程站点资源的可能性。
URI 的相对引用允许文档树在一定程度上独立于其位置和访问
方案。例如,如果文档之间使用相对引用相互引用,那么同一组
超文本文档可以同时通过 “file”、“http” 和 “ftp” 方案访问
并遍历。此外,这类文档树可以作为整体移动,而无需更改任何
相对引用。
相对引用(第 4.2 节)通过描述引用上下文与目标 URI 之间在
层次化名称空间中的差异来引用资源。第 5 节 给出的引用解析
Berners-Lee, et al. 标准轨道 [第 10 页]
RFC 3986 URI 通用语法 2005 年 1 月
算法定义了如何将这种引用转换为目标 URI。由于相对引用只能
在层次化 URI 的上下文中使用,新的 URI 方案设计者应使用与
通用语法层次组成部分一致的语法,除非有令人信服的理由禁止
在该方案中使用相对引用。
注:先前的规范使用术语“partial URI”和“relative URI”来
表示对 URI 的相对引用。由于有些读者误以为这些术语意味
着相对 URI 是 URI 的一个子集,而不是引用 URI 的一种方法,
本规范仅将其称为相对引用。
所有 URI 引用在使用时都由通用语法解析器解析。然而,由于
层次化处理对在引用中使用的绝对 URI 没有效果,除非它包含
一个或多个点段(由 “.” 或 “..” 构成的完整路径段,如
第 3.3 节 所述),URI 方案规范可以通过禁止使用斜杠字符、
问号字符以及 URI “scheme:.” 和 “scheme:..” 来定义不透明
标识符。
1.3. 语法表示法
本规范使用 [RFC2234] 的扩展巴科斯-瑙尔范式(ABNF)表示法,
包括该规范定义的以下核心 ABNF 语法规则:ALPHA(字母)、
CR(回车)、DIGIT(十进制数字)、DQUOTE(双引号)、
HEXDIG(十六进制数字)、LF(换行)和 SP(空格)。完整的
URI 语法汇总于 附录 A。
2. 字符
URI 语法提供了一种将数据编码为字符序列的方法,大概是为了
标识资源。URI 字符又经常被编码为八位组,用于传输或呈现。
本规范并不强制规定在 URI 字符与用于存储或传输这些字符的
八位组之间进行映射的任何特定字符编码。当 URI 出现在协议
元素中时,字符编码由该协议定义;如果没有这样的定义,则
假定 URI 与周围文本使用相同的字符编码。
ABNF 表示法将其终结值定义为基于 US-ASCII 编码字符集
[ASCII] 的非负整数(码点)。由于 URI 是一串字符,我们必须
反转这种关系,才能理解 URI 语法。因此,ABNF 所使用的整数
Berners-Lee, et al. 标准轨道 [第 11 页]
RFC 3986 URI 通用语法 2005 年 1 月
值必须通过 US-ASCII 映射回其对应字符,以完成语法规则。
URI 由有限字符集构成,包括数字、字母以及少量图形符号。
这些字符中的一个保留子集可用于界定 URI 中的语法组成部分;
而其余字符,包括非保留集合以及那些不充当定界符的保留
字符,则定义各组成部分的标识数据。
2.1. 百分号编码
当某个数据八位组对应的字符位于允许集合之外,或被用作该
组成部分的定界符或其内部的定界符时,会使用百分号编码
机制在该组成部分中表示该数据八位组。百分号编码的八位组
被编码为一个三字符组,由百分号字符 “%” 后跟两个表示该
八位组数值的十六进制数字组成。例如,“%20” 是二进制八位组
“00100000”(ABNF:%x20)的百分号编码,在 US-ASCII 中对应
空格字符(SP)。第 2.4 节 描述何时应用百分号编码和解码。
pct-encoded = "%" HEXDIG HEXDIG
大写十六进制数字 ‘A’ 到 ‘F’ 分别等价于小写数字 ‘a’ 到
‘f’。如果两个 URI 只在百分号编码八位组中使用的十六进制
数字大小写上不同,则它们是等价的。为保持一致性,URI
生产者和规范化器应为所有百分号编码使用大写十六进制数字。
2.2. 保留字符
URI 包含由“保留”集合中的字符界定的组成部分和子组成部分。
这些字符称为“保留”,因为它们可以(也可以不)由通用语法、
各个特定于方案的语法,或 URI 解引用算法的特定于实现的
语法定义为定界符。如果 URI 组成部分的数据会与某个保留
字符作为定界符的用途发生冲突,则在形成 URI 之前,必须
对该冲突数据进行百分号编码。
Berners-Lee, et al. 标准轨道 [第 12 页]
RFC 3986 URI 通用语法 2005 年 1 月
reserved = gen-delims / sub-delims
gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
/ "*" / "+" / "," / ";" / "="
保留字符的目的是提供一组定界字符,使其能够与 URI 中的其他
数据区分开来。将某个保留字符替换为其对应的百分号编码八位
组所得到的 URI,并不与原 URI 等价。对保留字符进行百分号
编码,或对对应于保留字符的百分号编码八位组进行解码,会
改变大多数应用对 URI 的解释方式。因此,保留集合中的字符
受到保护,不参与规范化,从而可安全地供特定于方案和特定于
生产者的算法用于界定 URI 内的数据子组成部分。
保留字符的一个子集(gen-delims)被用作 第 3 节 所述通用
URI 组成部分的定界符。组成部分的 ABNF 语法规则不会直接
使用 reserved 或 gen-delims 规则名;相反,每条语法规则会
列出该组成部分内允许出现的字符(即不对其进行界定的字符),
其中任何也属于保留集合的字符都被“保留”,用于在该组成部分
内作为子组成部分定界符。本规范只定义最常见的子组成部分;
其他子组成部分可由 URI 方案的规范定义,或由 URI 解引用
算法的特定于实现的语法定义,前提是这些子组成部分由该组成
部分内允许的保留集合字符进行界定。
URI 生产应用应对对应于保留集合中字符的数据八位组进行百分
号编码,除非 URI 方案明确允许这些字符在该组成部分中表示
数据。如果在 URI 组成部分中发现某个保留字符,且不知道该
字符有任何定界作用,则必须将其解释为表示该字符在 US-ASCII
中编码所对应的数据八位组。
2.3. 非保留字符
在 URI 中允许出现但没有保留用途的字符称为非保留字符。这些
字符包括大写和小写字母、十进制数字、连字符、句点、下划线
和波浪号。
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
Berners-Lee, et al. 标准轨道 [第 13 页]
RFC 3986 URI 通用语法 2005 年 1 月
将非保留字符替换为其对应的百分号编码 US-ASCII 八位组而
得到的 URI 是等价的:它们标识同一资源。然而,URI 比较实现
并不总是在比较前执行规范化(参见 第 6 节)。为保持一致性,
URI 生产者不应创建 ALPHA(%41-%5A 和 %61-%7A)、DIGIT
(%30-%39)、连字符(%2D)、句点(%2E)、下划线(%5F)或
波浪号(%7E)范围内的百分号编码八位组;当在 URI 中发现
这些八位组时,URI 规范化器应将其解码为对应的非保留字符。
2.4. 何时编码或解码
在正常情况下,URI 中八位组被百分号编码的唯一时机,是从
其组成部分生成 URI 的过程。在此过程中,实现会确定哪些保留
字符将作为子组成部分定界符使用,哪些可以安全地作为数据
使用。一旦生成,URI 始终处于其百分号编码形式。
当 URI 被解引用时,必须先解析并分离对特定于方案的解引用
过程(如果有)有意义的组成部分和子组成部分,然后才能安全地
解码这些组成部分内的百分号编码八位组;否则,数据可能会被
误认为组成部分定界符。唯一的例外是对应于非保留集合中字符
的百分号编码八位组,它们可以随时解码。例如,对应于波浪号
(“~”)字符的八位组常被较旧的 URI 处理实现编码为 “%7E”;
可以将 “%7E” 替换为 “~”,而不会改变其解释。
由于百分号(“%”)字符充当百分号编码八位组的指示符,如果
该八位组要作为 URI 内的数据使用,则必须将其百分号编码为
“%25”。实现不得对同一字符串进行多次百分号编码或解码,因为
对已经解码的字符串再次解码,可能导致将百分号数据八位组误
解释为百分号编码的开头;反之,对已经百分号编码的字符串
再次百分号编码也会产生类似问题。
2.5. 标识数据
URI 字符为每个 URI 组成部分提供标识数据,作为系统之间进行
标识的外部接口。尽管 URI 生产接口的存在和性质对使用其 URI
的客户端是隐藏的(因而超出了本规范所定义互操作性要求的
范围),但它经常成为解释 URI 字符问题时混淆和错误的来源。
实现者必须意识到,URI 的生成和传输涉及多种字符编码:本地
Berners-Lee, et al. 标准轨道 [第 14 页]
RFC 3986 URI 通用语法 2005 年 1 月
名称和数据编码、公共接口编码、URI 字符编码、数据格式编码
以及协议编码。
本地名称,例如文件系统名称,是以本地字符编码存储的。URI
生产应用(例如源服务器)通常会以本地编码作为生成有意义
名称的基础。URI 生产者会将本地编码转换为适合公共接口的
编码,然后再将公共接口编码转换为受限的 URI 字符集合
(保留字符、非保留字符和百分号编码)。这些字符又会被编码为
八位组,以作为数据格式(例如文档 charset)中的引用使用;
这类数据格式随后常常又被编码,以便通过 Internet 协议传输。
对大多数系统而言,出现在 URI 组成部分内的非保留字符被解释
为表示该字符在 US-ASCII 中编码所对应的数据八位组。URI
消费者假定字母 “X” 对应于八位组 “01011000”,即使这种假定
不正确,作出这种假定也没有危害。内部以不同字符编码形式
提供标识符的系统,例如 EBCDIC,通常会在内部接口处将文本
标识符字符转换为 UTF-8 [STD63](或 US-ASCII 字符编码的
其他超集),从而提供比简单地对原始八位组进行百分号编码
所得结果更有意义的标识符。
例如,考虑一个信息服务,它通过 HTTP 服务器向 Internet 上的
客户端提供本地使用基于 EBCDIC 的文件系统存储的数据。当作者
在该文件系统上创建名为 “Laguna Beach” 的文件时,对应该资源
的 “http” URI 预计会包含有意义的字符串 “Laguna%20Beach”。
然而,如果该服务器通过过于简单的原始八位组映射来生成 URI,
则结果会是一个包含 “%D3%81%87%A4%95%81@%C2%85%81%83%88” 的
URI。内部转码接口通过在生成 URI 之前,将本地名称转码为
US-ASCII 的超集来修正这一问题。自然,在这样的接口上正确
解释传入 URI,需要先解码百分号编码八位组(例如,将 “%20”
解码为 SP),然后再应用反向转码以获得本地名称。
在某些情况下,URI 组成部分与其被构造来表示的标识数据之间
的内部接口,远不如字符编码转换那样直接。例如,URI 的某些
部分可能反映对非 ASCII 数据的查询,或地图上的数字坐标。
Berners-Lee, et al. 标准轨道 [第 15 页]
RFC 3986 URI 通用语法 2005 年 1 月
同样,URI 方案可以定义具有附加编码要求的组成部分,这些要求
在形成组成部分并生成 URI 之前应用。
当新的 URI 方案定义一个表示来自通用字符集 [UCS] 的字符所
组成文本数据的组成部分时,应首先按照 UTF-8 字符编码
[STD63] 将该数据编码为八位组;然后,只应对那些不对应于
非保留集合中字符的八位组进行百分号编码。例如,字符 A 将
表示为 “A”,字符 LATIN CAPITAL LETTER A WITH GRAVE 将表示为
“%C3%80”,而字符 KATAKANA LETTER A 将表示为 “%E3%82%A2”。
3. 语法组成部分
通用 URI 语法由一个层次化的组成部分序列构成,这些组成部分
称为方案、授权机构、路径、查询和片段。
URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
hier-part = "//" authority path-abempty
/ path-absolute
/ path-rootless
/ path-empty
方案和路径组成部分是必需的,尽管路径可以为空(没有字符)。
当存在授权机构时,路径必须为空,或以斜杠(“/”)字符开始。
当不存在授权机构时,路径不能以两个斜杠字符(“//”)开始。
这些限制产生了五种不同的路径 ABNF 规则(第 3.3 节),
任意给定 URI 引用只会匹配其中一种。
以下是两个示例 URI 及其组成部分:
foo://example.com:8042/over/there?name=ferret#nose
\_/ \______________/\_________/ \_________/ \__/
| | | | |
scheme authority path query fragment
| _____________________|__
/ \ / \
urn:example:animal:ferret:nose
Berners-Lee, et al. 标准轨道 [第 16 页]
RFC 3986 URI 通用语法 2005 年 1 月
3.1. 方案
每个 URI 都以方案名开始,该方案名指向一份用于在该方案内
分配标识符的规范。因此,URI 语法是一种联合且可扩展的命名
系统,其中每个方案的规范都可以进一步限制使用该方案的标识符
的语法和语义。
方案名由一串字符组成,以字母开头,后面可跟字母、数字、
加号(“+”)、句点(“.”)或连字符(“-”)的任意组合。尽管
方案不区分大小写,其规范形式是小写,并且规定方案的文档必须
使用小写字母。为增强健壮性,实现应接受大写字母作为方案名中
小写字母的等价形式(例如,允许 “HTTP” 以及 “http”),但为
保持一致性,应只生成小写方案名。
scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
本文档不规定各个方案。新 URI 方案的注册过程由 [BCP35] 单独
定义。方案注册表维护方案名及其规范之间的映射。新 URI 方案
设计者可在 [RFC2718] 中找到相关建议。URI 方案规范必须定义
自己的语法,使所有匹配其特定于方案语法的字符串,也将匹配
第 4.3 节 所述的 <absolute-URI> 语法。
当遇到违反一个或多个特定于方案限制的 URI 时,特定于方案的
解析过程应将该引用标记为错误,而不是忽略未使用的部分;这样
做可以减少等价 URI 的数量,并有助于检测对通用语法的滥用,
这种滥用可能表明该 URI 被构造成用于误导用户(第 7.6 节)。
3.2. 授权机构
许多 URI 方案都包含一个用于命名授权机构的层次化元素,使得
由 URI 其余部分定义的名称空间治理可以委托给该授权机构
(而该授权机构又可以进一步委托)。通用语法提供了一种公共
方法,用于根据已注册名称或服务器地址来区分授权机构,并
包括可选的端口和用户信息。
授权机构组成部分前面带有双斜杠(“//”),并由下一个斜杠
(“/”)、问号(“?”)或数字符号(“#”)字符终止,或由 URI
结尾终止。
Berners-Lee, et al. 标准轨道 [第 17 页]
RFC 3986 URI 通用语法 2005 年 1 月
authority = [ userinfo "@" ] host [ ":" port ]
如果端口组成部分为空,URI 生产者和规范化器应省略用于分隔
host 与 port 的 “:” 定界符。某些方案不允许 userinfo 和/或
port 子组成部分。
如果 URI 包含授权机构组成部分,则路径组成部分必须为空,
或以斜杠(“/”)字符开始。非验证解析器(那些仅将 URI 引用
分离为主要组成部分的解析器)通常会忽略授权机构的子组成
结构,将其视为从双斜杠到第一个终止定界符之间的不透明字符串,
直到该 URI 被解引用时为止。
3.2.1. 用户信息
userinfo 子组成部分可以由用户名以及可选的、特定于方案的
信息组成,这些信息说明如何获得访问资源的授权。用户信息
如果存在,后面会跟一个商业 at 符号(“@”),用于将其与
host 分隔开。
userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
在 userinfo 字段中使用 “user:password” 格式已被弃用。
应用不应以明文形式呈现 userinfo 子组成部分内第一个冒号
(“:”)字符之后的任何数据,除非冒号之后的数据为空字符串
(表示没有密码)。应用在接收到此类数据作为引用的一部分时,
可以选择忽略或拒绝这些数据,并且应拒绝以未加密形式存储
此类数据。以明文传递认证信息,在几乎所有曾被使用的场景中
都已证明是一种安全风险。
为用户反馈而呈现 URI 的应用,例如图形化超文本浏览中的应用,
应在可行时以与 URI 其余部分可区分的方式呈现 userinfo。
这种呈现将有助于用户识别 userinfo 被恶意构造成看似可信
域名的情况(第 7.6 节)。
3.2.2. 主机
授权机构的 host 子组成部分通过方括号内的 IP 字面量、点分
十进制形式的 IPv4 地址,或已注册名称来标识。host 子组成
部分不区分大小写。URI 中存在 host 子组成部分,并不意味着
该方案要求通过 Internet 访问给定 host。在许多情况下,host
语法仅用于复用已经创建和部署的 DNS 注册过程,从而以无需
Berners-Lee, et al. 标准轨道 [第 18 页]
RFC 3986 URI 通用语法 2005 年 1 月
部署另一个注册表的成本获得全局唯一名称。然而,这种使用也有
自身代价:域名所有权可能会因 URI 生产者未预料到的原因随时间
变化。在其他情况下,host 组成部分内的数据标识的已注册名称,
可能与 Internet 主机毫无关系。我们将 ABNF 规则命名为 “host”,
是因为这是它最常见的用途,而不是它唯一的用途。
host = IP-literal / IPv4address / reg-name
host 的语法规则是有歧义的,因为它不能完全区分 IPv4address
和 reg-name。为消除语法歧义,我们应用“first-match-wins”
算法:如果 host 匹配 IPv4address 规则,则应将其视为 IPv4
地址字面量,而不是 reg-name。虽然 host 不区分大小写,但为
保持统一性,生产者和规范化器应对已注册名称和十六进制地址
使用小写,同时只在百分号编码中使用大写字母。
由 Internet 协议字面量地址(版本 6 [RFC3513] 或更高版本)
标识的 host,通过将 IP 字面量括在方括号(“[” 和 “]”)内来
区分。这是 URI 语法中唯一允许出现方括号字符的位置。为了预期
未来尚未定义的 IP 字面量地址格式,实现可以使用可选版本标志
来显式表示这种格式,而不是依赖启发式判断。
IP-literal = "[" ( IPv6address / IPvFuture ) "]"
IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
版本标志并不表示 IP 版本;相反,它表示字面量格式的未来版本。
因此,实现不得为下文描述的现有 IPv4 和 IPv6 字面量地址形式
提供版本标志。如果包含以 “v”(不区分大小写)开头的 IP-literal
的 URI 被某个不知道该版本标志含义的应用解引用,表明存在版本
标志,则该应用应返回“地址机制不受支持”的适当错误。
由 IPv6 字面量地址标识的 host 表示在方括号内,不带前置版本
标志。此处给出的 ABNF 是 [RFC3513] 中提供的 IPv6 字面量地址
文本定义的转换。该语法不支持 IPv6 作用域地址区标识符。
Berners-Lee, et al. 标准轨道 [第 19 页]
RFC 3986 URI 通用语法 2005 年 1 月
一个 128 位 IPv6 地址被划分为八个 16 位片段。每个片段以
不区分大小写的十六进制数值表示,使用一到四个十六进制数字
(允许前导零)。这八个编码片段按最高有效位优先给出,并以
冒号字符分隔。可选地,最低有效的两个片段也可以改用 IPv4
地址文本格式表示。地址中一个或多个连续的、值为零的 16 位
片段序列可以被省略,省略其所有数字,并在其位置留下恰好
两个连续冒号来标记该省略。
IPv6address = 6( h16 ":" ) ls32
/ "::" 5( h16 ":" ) ls32
/ [ h16 ] "::" 4( h16 ":" ) ls32
/ [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
/ [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
/ [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32
/ [ *4( h16 ":" ) h16 ] "::" ls32
/ [ *5( h16 ":" ) h16 ] "::" h16
/ [ *6( h16 ":" ) h16 ] "::"
ls32 = ( h16 ":" h16 ) / IPv4address
; 地址的最低有效 32 位
h16 = 1*4HEXDIG
; 以十六进制表示的地址 16 位
由 IPv4 字面量地址标识的主机以点分十进制表示法表示
(范围为 0 到 255 的四个十进制数字序列,以 “.” 分隔),
如 [RFC1123] 通过引用 [RFC0952] 所述。请注意,如
第 7.4 节 所述,某些平台可能会解释其他形式的点分表示法,
但本语法只允许四个八位组的点分十进制形式。
IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
dec-octet = DIGIT ; 0-9
/ %x31-39 DIGIT ; 10-99
/ "1" 2DIGIT ; 100-199
/ "2" %x30-34 DIGIT ; 200-249
/ "25" %x30-35 ; 250-255
由已注册名称标识的主机是一串字符,通常意在用于在本地定义的
主机或服务名称注册表中查找,尽管 URI 的特定于方案的语义
可能要求改用某个特定注册表(或固定名称表)。最常见的名称
注册机制是域名系统(DNS)。意在 DNS 中查找的已注册名称使用
Berners-Lee, et al. 标准轨道 [第 20 页]
RFC 3986 URI 通用语法 2005 年 1 月
[RFC1034] 第 3.5 节 和 [RFC1123] 第 2.1 节 所定义的语法。
这样的名称由一串以 “.” 分隔的域标签组成,每个域标签以字母
数字字符开头和结尾,并且也可能包含 “-” 字符。在 DNS 中,
完全限定域名最右侧的域标签后可以跟一个单独的 “.”;如果
需要区分完整域名和某个本地域名,则应这样做。
reg-name = *( unreserved / pct-encoded / sub-delims )
如果 URI 方案为 host 定义了默认值,则当 host 子组成部分
未定义或已注册名称为空(零长度)时,该默认值适用。例如,
“file” URI 方案被定义为:无授权机构、空 host 和 “localhost”
都表示最终用户的机器;而 “http” 方案则认为缺失授权机构或
空 host 是无效的。
本规范不强制规定某种特定的已注册名称查找技术,因此除了
互操作性所必需的内容之外,不限制 reg-name 的语法。相反,
它将已注册名称语法一致性问题委托给执行 URI 解析的各个
应用所在的操作系统,由该操作系统决定为了主机标识目的允许
什么内容。URI 解析实现可能使用 DNS、host 表、yellow pages、
NetInfo、WINS,或任何其他系统来查找已注册名称。然而,对于
意在具有全局范围的 URI,需要一个具有全局作用域的命名系统,
例如 DNS 完全限定域名。URI 生产者应使用符合 DNS 语法的名称,
即使 DNS 的使用并不明显,并且应将这些名称的长度限制为不超过
255 个字符。
reg-name 语法允许百分号编码的八位组,以便以一种独立于底层
名称解析技术的统一方式表示非 ASCII 已注册名称。非 ASCII
字符必须首先按照 UTF-8 [STD63] 进行编码,然后对应 UTF-8
序列的每个八位组都必须进行百分号编码,以表示为 URI 字符。
URI 生产应用不得在 host 中使用百分号编码,除非它用于表示
UTF-8 字符序列。当非 ASCII 已注册名称表示一个意在通过 DNS
解析的国际化域名时,该名称必须在名称查找之前转换为 IDNA
编码 [RFC3490]。如果 URI 生产者希望最大限度地与旧式 URI
解析器互操作,则应以 IDNA 编码提供这些已注册名称,而不是
以百分号编码提供。
Berners-Lee, et al. 标准轨道 [第 21 页]
RFC 3986 URI 通用语法 2005 年 1 月
3.2.3. 端口
授权机构的 port 子组成部分由一个可选端口号指定,该端口号
以十进制形式跟在 host 之后,并由单个冒号(“:”)字符与其
分隔。
port = *DIGIT
方案可以定义默认端口。例如,“http” 方案定义了默认端口
“80”,对应于其保留的 TCP 端口号。由端口号指定的端口类型
(例如 TCP、UDP、SCTP)由 URI 方案定义。如果 port 为空,或
其值与方案默认端口相同,则 URI 生产者和规范化器应省略 port
组成部分及其 “:” 定界符。
3.3. 路径
path 组成部分包含数据,这些数据通常以层次化形式组织,并与
非层次化 query 组成部分(第 3.4 节)中的数据一起,用于在
URI 的方案和命名授权机构(如果有)的范围内标识资源。path
由第一个问号(“?”)或数字符号(“#”)字符终止,或由 URI
结尾终止。
如果 URI 包含授权机构组成部分,则 path 组成部分必须为空,
或以斜杠(“/”)字符开始。如果 URI 不包含授权机构组成部分,
则 path 不能以两个斜杠字符(“//”)开始。此外,URI 引用
(第 4.1 节)可以是 relative-path 引用,在这种情况下,
第一个路径段不能包含冒号(“:”)字符。ABNF 需要五条单独规则
来消除这些情况的歧义,在给定 URI 引用中,只有其中一条规则
会匹配 path 子字符串。我们使用通用术语“path 组成部分”来描述
解析器匹配到这些规则之一的 URI 子字符串。
path = path-abempty ; 以 “/” 开始或为空
/ path-absolute ; 以 “/” 开始但不是 “//”
/ path-noscheme ; 以非冒号段开始
/ path-rootless ; 以一个段开始
/ path-empty ; 零个字符
path-abempty = *( "/" segment )
path-absolute = "/" [ segment-nz *( "/" segment ) ]
path-noscheme = segment-nz-nc *( "/" segment )
path-rootless = segment-nz *( "/" segment )
path-empty = 0<pchar>
Berners-Lee, et al. 标准轨道 [第 22 页]
RFC 3986 URI 通用语法 2005 年 1 月
segment = *pchar
segment-nz = 1*pchar
segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" )
; 不含任何冒号 “:” 的非零长度段
pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
path 由一串以斜杠(“/”)字符分隔的路径段组成。URI 始终定义
了 path,尽管所定义的 path 可以为空(零长度)。只有当 URI
将用作相对引用的上下文时,才要求使用斜杠字符来表示层次结构。
例如,URI <mailto:fred@example.com> 的 path 是 “fred@example.com”,
而 URI <foo://info.example.com?fred> 的 path 为空。
路径段 “.” 和 “..”,也称为点段,被定义为在路径名称层次结构
中进行相对引用。它们意在用于 relative-path 引用
(第 4.2 节)开头,以表示在名称层次树中的相对位置。
这类似于它们在某些操作系统文件目录结构中的作用,分别用于
表示当前目录和父目录。然而,与文件系统不同,这些点段只在
URI path 层次结构内解释,并作为解析过程的一部分被移除
(第 5.2 节)。
除了层次化路径中的点段之外,路径段在通用语法看来是不透明的。
URI 生产应用常常使用段中允许的保留字符,来界定特定于方案或
特定于解引用处理器的子组成部分。例如,分号(“;”)和等号
(“=”)保留字符常用于界定适用于该段的参数和参数值。逗号
(“,”)保留字符也常用于类似目的。例如,一个 URI 生产者可能
使用诸如 “name;v=1.1” 的段来表示对 “name” 的 1.1 版本的引用,
而另一个生产者可能使用诸如 “name,1.1” 的段来表示相同含义。
参数类型可以由特定于方案的语义定义,但在大多数情况下,参数
的语法特定于 URI 解引用算法的实现。
3.4. 查询
query 组成部分包含非层次化数据,这些数据与 path 组成部分
(第 3.3 节)中的数据一起,用于在 URI 的方案和命名授权
机构(如果有)的范围内标识资源。query 组成部分由第一个
问号(“?”)字符指示,并由数字符号(“#”)字符或 URI 结尾
终止。
Berners-Lee, et al. 标准轨道 [第 23 页]
RFC 3986 URI 通用语法 2005 年 1 月
query = *( pchar / "/" / "?" )
字符斜杠(“/”)和问号(“?”)可以在 query 组成部分中表示数据。
需要注意的是,某些较旧的错误实现,在这些数据被用作相对引用
的基准 URI(第 5.1 节)时,可能无法正确处理它们;显然这是
因为它们在查找层次分隔符时未能区分 query 数据和 path 数据。
然而,由于 query 组成部分常用于以 “key=value” 对的形式携带
标识信息,而一个经常使用的值是对另一个 URI 的引用,因此为了
可用性,有时最好避免对这些字符进行百分号编码。
3.5. 片段
URI 的 fragment identifier 组成部分允许通过引用主资源以及
附加标识信息,来间接标识次级资源。被标识的次级资源可以是
主资源的某个部分或子集、主资源表示上的某个视图,或由这些
表示定义或描述的其他资源。fragment identifier 组成部分由
数字符号(“#”)字符的出现来指示,并由 URI 结尾终止。
fragment = *( pchar / "/" / "?" )
fragment identifier 的语义由对主资源执行检索操作可能得到的
表示集合定义。因此,fragment 的格式和解析依赖于可能检索到的
表示的媒体类型 [RFC2046],即使只有在 URI 被解引用时才会执行
这种检索。如果不存在这样的表示,则 fragment 的语义被认为是
未知的,并且实际上不受约束。fragment identifier 语义独立于
URI 方案,因此不能由方案规范重新定义。
各个媒体类型可以为 fragment identifier 语法定义自己的限制或
内部结构,以指定该媒体类型可识别为次级资源的不同类型的子集、
视图或外部引用。如果主资源有多个表示,如同那些根据检索请求
的属性(又称内容协商)来选择表示的资源经常出现的情况,那么
fragment 所标识的内容应在所有这些表示之间保持一致。每个表示
都应将 fragment 定义为对应于同一个次级资源,而不论其如何表示;
或者应将 fragment 保持为未定义(即未找到)。
Berners-Lee, et al. 标准轨道 [第 24 页]
RFC 3986 URI 通用语法 2005 年 1 月
与任何 URI 一样,使用 fragment identifier 组成部分并不意味着
会发生检索操作。带有 fragment identifier 的 URI 可以用于引用
次级资源,而不暗示主资源可访问或将来会被访问。
fragment identifier 在信息检索系统中具有特殊作用,它是客户端
间接引用的主要形式,允许作者专门标识现有资源中那些仅由资源
所有者间接提供的方面。因此,fragment identifier 不用于 URI 的
特定于方案的处理;相反,在解引用之前,fragment identifier 会
与 URI 的其余部分分离,因此 fragment 本身内部的标识信息完全由
用户代理进行解引用,而不论 URI 方案是什么。虽然这种单独处理
通常被认为是一种信息损失,尤其是在资源随时间迁移时进行准确的
引用重定向方面,但它也用于防止信息提供者剥夺引用作者选择性
引用资源内信息的权利。间接引用还为使用 URI 的系统提供了额外
的灵活性和可扩展性,因为新的媒体类型比新的标识方案更容易
定义和部署。
字符斜杠(“/”)和问号(“?”)允许在 fragment identifier 中表示
数据。需要注意的是,某些较旧的错误实现,在这些数据被用作相对
引用的基准 URI(第 5.1 节)时,可能无法正确处理它们。
4. 用法
当应用引用 URI 时,并不总是使用由 “URI” 语法规则定义的完整
引用形式。为了节省空间并利用层次化局部性,许多 Internet
协议元素和媒体类型格式允许使用 URI 的缩写形式,而其他一些
则将语法限制为某种特定形式的 URI。我们在本规范中定义最常见
的引用语法形式,因为它们影响并依赖于通用语法的设计,需要
统一的解析算法才能被一致地解释。
4.1. URI 引用
URI-reference 用于表示资源标识符最常见的用法。
URI-reference = URI / relative-ref
Berners-Lee, et al. 标准轨道 [第 25 页]
RFC 3986 URI 通用语法 2005 年 1 月
URI-reference 要么是 URI,要么是相对引用。如果 URI-reference
的前缀不匹配一个方案后跟其冒号分隔符的语法,则该
URI-reference 是相对引用。
URI-reference 通常首先被解析为五个 URI 组成部分,以确定存在
哪些组成部分以及该引用是否为相对引用。然后,会解析每个组成
部分的子部分并进行验证。URI-reference 的 ABNF,加上
“first-match-wins” 消歧规则,足以定义一个用于通用语法的验证
解析器。熟悉正则表达式的读者可参见 附录 B,其中给出了一个
非验证 URI-reference 解析器的示例,该解析器会接受任意给定
字符串并提取 URI 组成部分。
4.2. 相对引用
相对引用利用层次化语法(第 1.2.3 节),相对于另一个层次化
URI 的名称空间来表达 URI 引用。
relative-ref = relative-part [ "?" query ] [ "#" fragment ]
relative-part = "//" authority path-abempty
/ path-absolute
/ path-noscheme
/ path-empty
由相对引用所引用的 URI,也称为目标 URI,通过应用
第 5 节 的引用解析算法获得。
以两个斜杠字符开头的相对引用称为 network-path 引用;这种引用
很少使用。以单个斜杠字符开头的相对引用称为 absolute-path
引用。不以斜杠字符开头的相对引用称为 relative-path 引用。
包含冒号字符的路径段(例如 “this:that”)不能用作 relative-path
引用的第一段,因为它会被误认为是方案名。这样的段必须前置
一个点段(例如 “./this:that”),以形成 relative-path 引用。
Berners-Lee, et al. 标准轨道 [第 26 页]
RFC 3986 URI 通用语法 2005 年 1 月
4.3. 绝对 URI
某些协议元素只允许不带 fragment identifier 的 URI 绝对形式。
例如,为供以后由相对引用使用而定义基准 URI,就需要一个不允许
fragment 的 absolute-URI 语法规则。
absolute-URI = scheme ":" hier-part [ "?" query ]
URI 方案规范必须定义自己的语法,使所有匹配其特定于方案语法的
字符串,也将匹配 <absolute-URI> 语法。方案规范不会定义 fragment
identifier 的语法或用法,不论其是否适用于可通过该方案标识的
资源,因为 fragment 标识与方案定义正交。不过,鼓励方案规范
包含范围广泛的示例,包括在适当时展示该方案 URI 与 fragment
identifier 一起使用的示例。
4.4. 同文档引用
当 URI 引用所指向的 URI 除 fragment 组成部分(如果有)之外,
与基准 URI(第 5.1 节)相同时,该引用称为“same-document”
引用。same-document 引用最常见的示例是空的相对引用,或仅包含
数字符号(“#”)分隔符后跟 fragment identifier 的相对引用。
当为检索操作而解引用 same-document 引用时,该引用的目标被定义
为位于与该引用相同的实体(表示、文档或消息)内;因此,解引用
不应导致新的检索操作。
如第 6.2.2 节和第 6.2.3 节所述,允许在比较前对基准 URI
和目标 URI 进行规范化,但实践中很少这样做。规范化可能扩大
same-document 引用集合,这可能对某些缓存应用有益。因此,引用
作者不应假定某个略有不同但等价的引用 URI 会(或不会)被任何
给定应用解释为 same-document 引用。
4.5. 后缀引用
URI 语法被设计用于对资源进行无歧义引用,并通过 URI 方案实现
可扩展性。然而,随着 URI 标识和使用变得普遍,传统媒体(电视、
广播、报纸、广告牌等)越来越多地使用 URI 的后缀作为引用,
Berners-Lee, et al. 标准轨道 [第 27 页]
RFC 3986 URI 通用语法 2005 年 1 月
仅由 URI 的 authority 和 path 部分组成,例如
www.w3.org/Addressing/
或者仅仅是一个 DNS 已注册名称本身。这类引用主要意在供人类
解释,而不是供机器解释,其假设是基于上下文的启发式方法足以
补全 URI(例如,大多数以 “www” 开头的已注册名称很可能具有
“http://” URI 前缀)。虽然没有用于消除 URI 后缀歧义的标准
启发式集合,但许多客户端实现允许用户输入它们,并以启发式方式
解析。
尽管使用后缀引用的做法很常见,但应尽可能避免,并且绝不应在
期望长期引用的情况下使用。上面提到的启发式方法会随时间变化,
特别是当新的 URI 方案流行起来时,并且在脱离上下文使用时常常
是错误的。此外,它们还可能导致类似 [RFC1535] 所述的安全问题。
由于 URI 后缀与 relative-path 引用具有相同语法,因此在期望
相对引用的上下文中不能使用后缀引用。因此,后缀引用仅限于没有
已定义基准 URI 的地方,例如对话框和离线广告。
5. 引用解析
本节定义在允许相对引用的上下文中解析 URI 引用的过程,使其
结果成为匹配 第 3 节 <URI> 语法规则的字符串。
5.1. 建立基准 URI
术语“relative”意味着存在一个“base URI”,相对引用将针对它
应用。除仅含 fragment 的引用(第 4.4 节)之外,相对引用
只有在基准 URI 已知时才可使用。在解析可能为相对形式的 URI
引用之前,解析器必须建立基准 URI。基准 URI 必须符合
<absolute-URI> 语法规则(第 4.3 节)。如果基准 URI 是从
URI 引用获得的,则在将其用作基准 URI 之前,必须将该引用
转换为绝对形式并移除任何 fragment 组成部分。
Berners-Lee, et al. 标准轨道 [第 28 页]
RFC 3986 URI 通用语法 2005 年 1 月
引用的基准 URI 可以通过以下四种方式之一建立,下面按优先级顺序
讨论。优先级顺序可被理解为层的关系,其中最内层定义的基准 URI
具有最高优先级。其图形化表示如下:
.----------------------------------------------------------.
| .----------------------------------------------------. |
| | .----------------------------------------------. | |
| | | .----------------------------------------. | | |
| | | | .----------------------------------. | | | |
| | | | | <relative-reference> | | | | |
| | | | `----------------------------------' | | | |
| | | | (5.1.1) 嵌入内容中的基准 URI | | | |
| | | `----------------------------------------' | | |
| | | (5.1.2) 封装实体的基准 URI | | |
| | | (消息、表示,或无) | | |
| | `----------------------------------------------' | |
| | (5.1.3) 用于检索实体的 URI | |
| `----------------------------------------------------' |
| (5.1.4) 默认基准 URI(取决于应用) |
`----------------------------------------------------------'
5.1.1. 嵌入内容中的基准 URI
在某些媒体类型中,相对引用的基准 URI 可以嵌入内容本身,使解析器
能够方便地获取它。这对于描述性文档可能很有用,例如目录,这类
文档可能通过不同于其通常检索上下文的协议(例如电子邮件或
USENET 新闻)传输给他人。
本规范不负责规定如何针对每种媒体类型嵌入基准 URI。可用时,
适当语法由与各媒体类型关联的数据格式规范描述。
5.1.2. 来自封装实体的基准 URI
如果没有嵌入基准 URI,则基准 URI 由表示的检索上下文定义。对于
被包含在另一个实体(例如消息或归档)中的文档,检索上下文就是
该实体。因此,表示的默认基准 URI 是封装该表示的实体的基准 URI。
Berners-Lee, et al. 标准轨道 [第 29 页]
RFC 3986 URI 通用语法 2005 年 1 月
MHTML [RFC2557] 定义了一种在 MIME 容器类型(例如 message 和
multipart 类型)内嵌入基准 URI 的机制。不使用 MIME 消息头语法,
但允许在消息内包含某种形式带标签元数据的协议,可以定义自己的
语法,将基准 URI 定义为消息的一部分。
5.1.3. 来自检索 URI 的基准 URI
如果没有嵌入基准 URI,且表示未被封装在其他某个实体中,那么
如果曾使用某个 URI 来检索该表示,该 URI 应被视为基准 URI。
请注意,如果检索是重定向请求的结果,则最后使用的 URI(即实际
检索到该表示的 URI)就是基准 URI。
5.1.4. 默认基准 URI
如果上述条件均不适用,则基准 URI 由应用的上下文定义。由于此
定义必然取决于应用,未能使用其他方法之一来定义基准 URI,可能
导致同一内容被不同类型的应用以不同方式解释。
含有相对引用的表示的发送者,有责任确保能够为这些引用建立基准
URI。除仅含 fragment 的引用之外,相对引用只有在基准 URI 定义
明确的情况下才能可靠使用。
5.2. 相对解析
本节描述一种算法,用于将可能为相对形式的 URI 引用,针对给定
基准 URI 转换为该引用目标的已解析组成部分。随后可以按
第 5.3 节 所述重新组合这些组成部分,以形成目标 URI。该算法
提供确定性结果,可用于测试其他实现的输出。应用可以使用其他
算法实现相对引用解析,前提是其结果与本算法给出的结果相匹配。
Berners-Lee, et al. 标准轨道 [第 30 页]
RFC 3986 URI 通用语法 2005 年 1 月
5.2.1. 预解析基准 URI
基准 URI(Base)按照 第 5.1 节 的过程建立,并被解析为
第 3 节 所述的五个主要组成部分。请注意,基准 URI 中只要求
存在 scheme 组成部分;其他组成部分可以为空或未定义。如果组成
部分的关联定界符未出现在 URI 引用中,则该组成部分未定义;
path 组成部分从不未定义,尽管它可以为空。
按第 6.2.2 节和第 6.2.3 节所述,对基准 URI 进行规范化是
可选的。URI 引用必须先转换为其目标 URI,之后才能规范化。
5.2.2. 转换引用
对于每个 URI 引用(R),以下伪代码描述了将 R 转换为其目标
URI(T)的算法:
-- URI 引用被解析为五个 URI 组成部分
--
(R.scheme, R.authority, R.path, R.query, R.fragment) = parse(R);
-- 如果引用中的 scheme 与基准 URI 的 scheme 相同,
-- 非严格解析器可以忽略引用中的 scheme。
--
if ((not strict) and (R.scheme == Base.scheme)) then
undefine(R.scheme);
endif;
Berners-Lee, et al. 标准轨道 [第 31 页]
RFC 3986 URI 通用语法 2005 年 1 月
if defined(R.scheme) then
T.scheme = R.scheme;
T.authority = R.authority;
T.path = remove_dot_segments(R.path);
T.query = R.query;
else
if defined(R.authority) then
T.authority = R.authority;
T.path = remove_dot_segments(R.path);
T.query = R.query;
else
if (R.path == "") then
T.path = Base.path;
if defined(R.query) then
T.query = R.query;
else
T.query = Base.query;
endif;
else
if (R.path starts-with "/") then
T.path = remove_dot_segments(R.path);
else
T.path = merge(Base.path, R.path);
T.path = remove_dot_segments(T.path);
endif;
T.query = R.query;
endif;
T.authority = Base.authority;
endif;
T.scheme = Base.scheme;
endif;
T.fragment = R.fragment;
5.2.3. 合并路径
上面的伪代码引用了一个 “merge” 例程,用于将 relative-path 引用
与基准 URI 的 path 合并。其完成方式如下:
o 如果基准 URI 具有已定义的 authority 组成部分和空 path,则
返回由 “/” 与引用的 path 串接而成的字符串;否则,
Berners-Lee, et al. 标准轨道 [第 32 页]
RFC 3986 URI 通用语法 2005 年 1 月
o 返回一个字符串,该字符串由引用的 path 组成部分追加到基准 URI
path 中除最后一段以外的所有内容之后(即,排除基准 URI path
中最右侧 “/” 之后的任何字符;如果基准 URI path 不包含任何
“/” 字符,则排除整个基准 URI path)。
5.2.4. 移除点段
伪代码还引用了一个 “remove_dot_segments” 例程,用于解释并移除
被引用路径中的特殊完整路径段 “.” 和 “..”。这是在从引用中提取
path 之后完成的,无论该 path 是否为相对路径,以便在形成目标
URI 之前移除任何无效或多余的点段。虽然有许多方法可以完成这种
移除过程,但我们描述一种使用两个字符串缓冲区的简单方法。
1. 输入缓冲区用现在已追加的 path 组成部分初始化,输出缓冲区
初始化为空字符串。
2. 当输入缓冲区非空时,按如下方式循环:
A. 如果输入缓冲区以 "../" 或 "./" 前缀开始,则从输入
缓冲区中移除该前缀;否则,
B. 如果输入缓冲区以 "/./" 或 "/." 前缀开始,其中 "." 是
一个完整路径段,则用 "/" 替换输入缓冲区中的该前缀;
否则,
C. 如果输入缓冲区以 "/../" 或 "/.." 前缀开始,其中 ".."
是一个完整路径段,则用 "/" 替换输入缓冲区中的该前缀,
并从输出缓冲区中移除最后一个段及其前面的 "/"(如果有);
否则,
D. 如果输入缓冲区仅由 "." 或 ".." 构成,则从输入缓冲区中
移除它;否则,
E. 将输入缓冲区中的第一个路径段移动到输出缓冲区末尾,
包括初始的 "/" 字符(如果有),以及直到但不包括下一个
"/" 字符或输入缓冲区结尾的所有后续字符。
3. 最后,返回输出缓冲区作为 remove_dot_segments 的结果。
Berners-Lee, et al. 标准轨道 [第 33 页]
RFC 3986 URI 通用语法 2005 年 1 月
请注意,点段意在用于 URI 引用中,以表达一个相对于基准 URI 中
名称层次结构的标识符。remove_dot_segments 算法通过移除额外的
点段来尊重该层次结构,而不是将它们视为错误,或任由它们被解引
用实现误解释。
下面展示了如何将上述步骤应用于两个合并路径示例,显示每一步后
两个缓冲区的状态。
步骤 输出缓冲区 输入缓冲区
1 : /a/b/c/./../../g
2E: /a /b/c/./../../g
2E: /a/b /c/./../../g
2E: /a/b/c /./../../g
2B: /a/b/c /../../g
2C: /a/b /../g
2C: /a /g
2E: /a/g
步骤 输出缓冲区 输入缓冲区
1 : mid/content=5/../6
2E: mid /content=5/../6
2E: mid/content=5 /../6
2C: mid /6
2E: mid/6
某些应用可能会发现,使用两个段栈而不是字符串来实现
remove_dot_segments 算法会更高效。
注:需要注意的是,某些较旧的错误实现会在合并基准 path 和
引用 path 之前,未能将引用的 query 组成部分与其 path 组成
部分分离;如果 query 组成部分包含字符串 "/../" 或 "/./",
这将导致互操作性失败。
Berners-Lee, et al. 标准轨道 [第 34 页]
RFC 3986 URI 通用语法 2005 年 1 月
5.3. 组成部分重组
已解析的 URI 组成部分可以重新组合,以获得对应的 URI 引用字符串。
使用伪代码表示如下:
result = ""
if defined(scheme) then
append scheme to result;
append ":" to result;
endif;
if defined(authority) then
append "//" to result;
append authority to result;
endif;
append path to result;
if defined(query) then
append "?" to result;
append query to result;
endif;
if defined(fragment) then
append "#" to result;
append fragment to result;
endif;
return result;
请注意,我们谨慎地保留未定义组成部分与空组成部分之间的区别:
未定义意味着其分隔符未出现在引用中;空则意味着分隔符已出现,
且其后紧跟下一个组成部分分隔符或引用结尾。
5.4. 引用解析示例
在具有明确定义的基准 URI 的表示中,即
http://a/b/c/d;p?q
相对引用按如下方式转换为其目标 URI。
Berners-Lee, et al. 标准轨道 [第 35 页]
RFC 3986 URI 通用语法 2005 年 1 月
5.4.1. 正常示例
"g:h" = "g:h"
"g" = "http://a/b/c/g"
"./g" = "http://a/b/c/g"
"g/" = "http://a/b/c/g/"
"/g" = "http://a/g"
"//g" = "http://g"
"?y" = "http://a/b/c/d;p?y"
"g?y" = "http://a/b/c/g?y"
"#s" = "http://a/b/c/d;p?q#s"
"g#s" = "http://a/b/c/g#s"
"g?y#s" = "http://a/b/c/g?y#s"
";x" = "http://a/b/c/;x"
"g;x" = "http://a/b/c/g;x"
"g;x?y#s" = "http://a/b/c/g;x?y#s"
"" = "http://a/b/c/d;p?q"
"." = "http://a/b/c/"
"./" = "http://a/b/c/"
".." = "http://a/b/"
"../" = "http://a/b/"
"../g" = "http://a/b/g"
"../.." = "http://a/"
"../../" = "http://a/"
"../../g" = "http://a/g"
5.4.2. 异常示例
尽管以下异常示例在正常实践中不太可能出现,但所有 URI 解析器都
应能够一致地解析它们。每个示例都使用上面相同的基准。
解析器在处理 relative-path 引用中 “..” 段数量多于基准 URI 的
path 中层次级数的情况时必须小心。请注意,“..” 语法不能用于
改变 URI 的 authority 组成部分。
"../../../g" = "http://a/g"
"../../../../g" = "http://a/g"
Berners-Lee, et al. 标准轨道 [第 36 页]
RFC 3986 URI 通用语法 2005 年 1 月
类似地,解析器必须在 “.” 和 “..” 是 path 的完整组成部分时移除
这些点段,但当它们只是某个段的一部分时则不能移除。
"/./g" = "http://a/g"
"/../g" = "http://a/g"
"g." = "http://a/b/c/g."
".g" = "http://a/b/c/.g"
"g.." = "http://a/b/c/g.."
"..g" = "http://a/b/c/..g"
还不太可能出现的情况是,相对引用使用了不必要或无意义形式的
“.” 和 “..” 完整路径段。
"./../g" = "http://a/b/g"
"./g/." = "http://a/b/c/g/"
"g/./h" = "http://a/b/c/g/h"
"g/../h" = "http://a/b/c/h"
"g;x=1/./y" = "http://a/b/c/g;x=1/y"
"g;x=1/../y" = "http://a/b/c/y"
某些应用在将引用的 path 组成部分与基准 path 合并并移除点段之前,
未能将引用的 query 和/或 fragment 组成部分与 path 组成部分分离。
这种错误很少被注意到,因为 fragment 的典型用法从不包含层次
(“/”)字符,而 query 组成部分通常不在相对引用内使用。
"g?y/./x" = "http://a/b/c/g?y/./x"
"g?y/../x" = "http://a/b/c/g?y/../x"
"g#s/./x" = "http://a/b/c/g#s/./x"
"g#s/../x" = "http://a/b/c/g#s/../x"
某些解析器允许在相对引用中出现 scheme 名,前提是它与基准 URI
scheme 相同。这被认为是先前 partial URI 规范 [RFC1630] 中
的一个漏洞。应避免使用它,但为了向后兼容而允许。
"http:g" = "http:g" ; 对于严格解析器
/ "http://a/b/c/g" ; 为了向后兼容
Berners-Lee, et al. 标准轨道 [第 37 页]
RFC 3986 URI 通用语法 2005 年 1 月
6. 规范化与比较
URI 上最常见的操作之一是简单比较:在不使用这些 URI 访问其各自
资源的情况下,确定两个 URI 是否等价。每当访问响应缓存、浏览器
检查其历史以给链接着色,或 XML 解析器处理命名空间内的标签时,
都会执行比较。爬虫和索引引擎通常在比较 URI 之前进行广泛的
规范化,以修剪搜索空间,或减少请求操作和响应存储的重复。
URI 比较是为了某个特定目的而执行的。为不同目的比较 URI 的协议
或实现,在应花费多少努力来减少别名标识符方面,通常会受到不同
设计权衡的影响。本节描述可用于比较 URI 的各种方法、它们之间的
权衡,以及可能使用它们的应用类型。
6.1. 等价性
由于 URI 的存在是为了标识资源,因此大概当它们标识同一资源时,
就应被视为等价。然而,这种等价定义在实践中用处不大,因为实现
无法比较两个资源,除非它完全了解或控制它们。因此,对 URI 等价
或差异的判定基于字符串比较,可能还会借助 URI 方案定义提供的
附加规则来增强。我们使用术语“不同”和“等价”来描述这类比较的
可能结果,但存在许多取决于应用的等价版本。
即使可以判定两个 URI 等价,URI 比较也不足以判定两个 URI 是否
标识不同资源。例如,两个不同域名的所有者可以决定从二者提供同
一个资源,从而产生两个不同的 URI。因此,比较方法被设计为在
严格避免误判为等价的同时,尽量减少误判为不等价。
在测试等价性时,应用不应直接比较相对引用;在比较之前,应将这些
引用转换为各自的目标 URI。当比较 URI 是为了选择(或避免)网络
操作,例如检索某个表示时,应从比较中排除 fragment 组成部分
(如果有)。
Berners-Lee, et al. 标准轨道 [第 38 页]
RFC 3986 URI 通用语法 2005 年 1 月
6.2. 比较阶梯
实践中使用多种方法来测试 URI 等价性。这些方法形成一个范围,
区别在于所需处理量,以及误判为不等价的概率被降低的程度。如上
所述,误判为不等价无法被消除。实践中可以降低其概率,但这种
降低需要更多处理,并非对所有应用都划算。
如果把这种比较实践的范围看作一架梯子,下面的讨论将沿着梯子
向上,从成本低但产生误判为不等价的可能性相对较高的实践开始,
逐步到达计算成本较高但误判为不等价风险较低的实践。
6.2.1. 简单字符串比较
如果两个 URI 在作为字符串考虑时完全相同,则可以安全地得出它们
等价的结论。这类等价性测试的计算成本非常低,并且在各种应用中
被广泛使用,尤其是在解析领域。
测试字符串等价性需要一些基本注意事项。此过程通常被称为
“bit-for-bit” 或 “byte-for-byte” 比较,这可能会产生误导。测试
字符串相等通常基于对组成字符串的字符进行成对比较,从第一个字符
开始,直到两个字符串都耗尽且所有字符均相等,或直到某一对字符
比较不相等,或直到其中一个字符串先于另一个字符串耗尽。
这种字符比较要求每对字符都处于可比较的形式。例如,如果一个 URI
存储在 EBCDIC 编码的字节数组中,而第二个 URI 存储在 Java String
对象(UTF-16)中,天真地应用 bit-for-bit 比较会产生错误。最好
谈论逐字符意义上的相等,而不是逐字节或逐位意义上的相等。实际
上,逐字符比较应在转换为公共字符编码后,按码点逐码点进行。
误判为不等价是由 URI 别名的生成和使用造成的。无论采用哪种比较
方法,都可以通过始终以已规范化形式提供 URI 引用来减少不必要的
别名(即,与如下所述应用规范化后会产生的形式相同的形式)。
Berners-Lee, et al. 标准轨道 [第 39 页]
RFC 3986 URI 通用语法 2005 年 1 月
协议和数据格式通常会将某些 URI 比较限制为简单字符串比较,其理论
是人们和实现会出于自身最大利益而在提供 URI 引用时保持一致,或
至少足够一致,以抵消进一步规范化可能带来的任何效率收益。
6.2.2. 基于语法的规范化
实现可以使用基于本规范所提供定义的逻辑来降低误判为不等价的
概率。此处理的成本适度高于逐字符字符串比较。例如,使用这种方法
的应用可以合理地认为以下两个 URI 等价:
example://a/b/c/%7Bfoo%7D
eXAMPLE://a/./b/../b/%63/%7bfoo%7d
Web 用户代理,例如浏览器,通常在确定是否有可用缓存响应时应用
这类 URI 规范化。基于语法的规范化包括大小写规范化、百分号编码
规范化,以及移除点段等技术。
6.2.2.1. 大小写规范化
对于所有 URI,百分号编码三元组内的十六进制数字(例如,“%3a”
与 “%3A”)不区分大小写,因此应规范化为对数字 A-F 使用大写字母。
当 URI 使用通用语法的组成部分时,组成部分语法等价规则始终适用;
也就是说,scheme 和 host 不区分大小写,因此应规范化为小写。
例如,URI <HTTP://www.EXAMPLE.com/> 等价于
<http://www.example.com/>。除非方案另有明确定义,其他通用语法
组成部分被假定为区分大小写(参见 第 6.2.3 节)。
6.2.2.2. 百分号编码规范化
百分号编码机制(第 2.1 节)是本应相同的 URI 之间经常出现
差异的来源。除了上面提到的大小写规范化问题外,一些 URI 生产者
会对不需要百分号编码的八位组进行百分号编码,从而产生与其未编码
对应形式等价的 URI。这些 URI 应通过解码任何对应于非保留字符的
百分号编码八位组来规范化,如 第 2.3 节 所述。
Berners-Lee, et al. 标准轨道 [第 40 页]
RFC 3986 URI 通用语法 2005 年 1 月
6.2.2.3. 路径段规范化
完整路径段 “.” 和 “..” 仅意在用于相对引用(第 4.1 节),
并作为引用解析过程(第 5.2 节)的一部分被移除。然而,一些
已部署的实现错误地假定,当引用已经是 URI 时就不需要进行引用
解析,因此当点段出现在非相对路径中时,未能移除它们。URI
规范化器应按 第 5.2.4 节 所述,将 remove_dot_segments 算法
应用于 path,以移除点段。
6.2.3. 基于方案的规范化
URI 的语法和语义因方案而异,如每个方案的定义规范所描述。
实现可以使用特定于方案的规则,以进一步处理成本来降低误判为
不等价的概率。例如,由于 “http” 方案使用 authority 组成部分,
具有默认端口 “80”,并定义空 path 等价于 “/”,因此以下四个 URI
是等价的:
http://example.com
http://example.com/
http://example.com:/
http://example.com:80/
一般而言,使用通用 authority 语法且 path 为空的 URI,应规范化
为 path 为 “/”。同样,显式的 “:port”,如果 port 为空或为方案的
默认端口,就等价于省略 port 及其 “:” 定界符的形式,因此应通过
基于方案的规范化移除。例如,上面的第二个 URI 是 “http” 方案的
规范形式。
规范化因方案而异的另一种情况,是对空 authority 组成部分或空
host 子组成部分的处理。对于许多方案规范,空 authority 或 host
被视为错误;对于其他方案,则被视为等价于 “localhost” 或最终
用户的主机。当方案为 authority 定义默认值,并且需要对该默认值
的 URI 引用时,为了统一、简洁和国际化,该引用应规范化为空
authority。然而,如果 userinfo 或 port 子组成部分非空,则即使
host 与默认值匹配,也应显式给出 host。
规范化不应在关联组成部分为空时移除定界符,除非方案规范许可
Berners-Lee, et al. 标准轨道 [第 41 页]
RFC 3986 URI 通用语法 2005 年 1 月
这样做。例如,不能假定 URI “http://example.com/?” 等价于上面
任何一个示例。同样,userinfo 子组成部分中定界符的存在与否,
通常对其解释很重要。fragment 组成部分不受任何基于方案的
规范化影响;因此,两个仅因后缀 “#” 不同的 URI,无论方案如何,
都被视为不同。
某些方案定义了由不区分大小写的数据组成的附加子组成部分,从而
隐式许可规范化器将这些数据转换为统一大小写(例如全部小写)。
例如,定义 path 的某个子组成部分包含 Internet 主机名的 URI
方案,如 “mailto” URI 方案,会使该子组成部分不区分大小写,
因而受大小写规范化影响(例如,“mailto:Joe@Example.COM”等价于
“mailto:Joe@example.com”,尽管通用语法认为 path 组成部分区分
大小写)。
其他特定于方案的规范化也是可能的。
6.2.4. 基于协议的规范化
对于 Web 爬虫而言,投入大量努力来减少误判为不等价的发生通常
是划算的。因此,它们在 URI 比较中会实现更激进的技术。例如,
如果它们观察到某个 URI,例如
http://example.com/data
重定向到一个仅在尾随斜杠上不同的 URI
http://example.com/data/
它们很可能会在未来将二者视为等价。只有当访问资源的结果以及
其方案解引用算法的通用约定都明确表明等价时,这类技术才适用
(在此情况下,即 HTTP 源服务器使用重定向以避免相对引用问题)。
Berners-Lee, et al. 标准轨道 [第 42 页]
RFC 3986 URI 通用语法 2005 年 1 月
7. 安全考虑
URI 本身并不构成安全威胁。然而,由于 URI 经常用于提供一组
紧凑的访问网络资源的指令,因此必须谨慎地正确解释 URI 内的
数据,防止这些数据导致非预期访问,并避免包含不应以明文泄露的
数据。
7.1. 可靠性与一致性
不能保证一旦某个 URI 被用于检索信息,将来仍能通过该 URI 检索到
相同信息。也不能保证将来可通过该 URI 检索到的信息,与过去检索到
的信息在可观察上相似。URI 语法并不约束给定方案或 authority 如何
分配其名称空间,或如何随时间维护它。此类保证只能由控制该名称
空间和相关资源的人获得。如果某个特定 URI 方案要求该方案的所有
命名授权机构都具备某些语义,例如名称持久性,则该方案可以定义
这些附加语义。
7.2. 恶意构造
有时可以构造一个 URI,使得尝试执行一个看似无害、幂等的操作
(例如检索某个表示)实际上会导致可能具有破坏性的远程操作。
不安全的 URI 通常通过指定一个不同于相关网络协议所保留端口的
端口号来构造。客户端在不知情的情况下联系运行不同协议服务的
站点,而 URI 内的数据包含一些指令,这些指令按照该其他协议解释时
会导致意外操作。此类滥用的一个常见示例,是使用带有端口组成部分
“25” 的基于协议的方案,从而欺骗用户代理软件通过 SMTP 服务器发送
非预期消息或冒名消息。
应用应阻止解引用指定 TCP 端口号位于“知名端口”范围(0 - 1023)
内的 URI,除非用于解引用该 URI 的协议与该知名端口上所期望的
协议兼容。虽然 IANA 维护着知名端口注册表,但应用应使此类限制
可由用户配置,以避免阻碍新服务的部署。
Berners-Lee, et al. 标准轨道 [第 43 页]
RFC 3986 URI 通用语法 2005 年 1 月
当 URI 包含与给定解析或解引用协议的定界符相匹配的百分号编码
八位组时(例如 TELNET 协议的 CR 和 LF 字符),这些百分号编码
在跨该协议传输之前不得被解码。传输可能违反协议的百分号编码,
比允许已解码八位组被解释为附加操作或参数危害更小;后者可能
触发意外且可能有害的远程操作。
7.3. 后端转码
当 URI 被解引用时,其中的数据通常会同时由用户代理和一个或多个
服务器解析。例如,在 HTTP 中,典型的用户代理会将 URI 解析为其
五个主要组成部分,访问 authority 的服务器,并向其发送 authority、
path 和 query 组成部分内的数据。典型服务器会获取这些信息,将
path 解析为段,将 query 解析为键/值对,然后调用特定于实现的
处理器来响应请求。因此,对于处理 URI 的服务器实现而言,无论是
将 URI 作为整体处理,还是拆分为单独组成部分处理,一个常见的
安全关注点是正确解释由该 URI 内的字符和百分号编码所表示的
八位组数据。
百分号编码的八位组必须在解引用过程的某个时刻被解码。应用必须
在解码八位组之前,将 URI 拆分为其组成部分和子组成部分;否则,
已解码的八位组可能会被误认为定界符。对 URI 内数据的安全检查
应在解码八位组之后应用。不过请注意,“%00” 百分号编码(NUL)
可能需要特殊处理;如果应用并不期望在某个组成部分内接收原始
数据,则应拒绝它。
当 URI path 的解释过程涉及使用后端文件系统或相关系统函数时,
应特别小心。文件系统通常会为特殊字符赋予操作含义,例如 “/”、
“\”、“:”、“[” 和 “]” 字符,以及诸如 “.”、“..”、“...”、“aux”、
“lpt”等特殊设备名称。在某些情况下,仅测试这类名称是否存在就会
导致操作系统暂停或调用无关的系统调用,从而引发关于拒绝服务和
非预期数据传输的重大安全关注。本规范不可能列出所有此类重要字符
和设备名称。实现者应研究可能附加到其应用的存储设备类型中的
保留名称和字符,并相应地限制从 URI 组成部分获得的数据的使用。
Berners-Lee, et al. 标准轨道 [第 44 页]
RFC 3986 URI 通用语法 2005 年 1 月
7.4. 罕见 IP 地址格式
虽然 URI 的 IPv4address 语法只允许常见的 IPv4 地址字面量点分
十进制形式,但许多处理 URI 的实现会使用取决于平台的系统例程,
例如 gethostbyname() 和 inet_aton(),来将字符串字面量转换为
实际 IP 地址。不幸的是,这类系统例程经常允许并处理比
第 3.2.2 节 所述更大范围的格式。
例如,许多实现允许三个数字的点分形式,其中最后一部分被解释为
16 位数量,并放置在网络地址最右侧的两个字节中(例如 Class B
网络)。同样,两个数字的点分形式意味着最后一部分被解释为 24 位
数量,并放置在网络地址最右侧的三个字节中(Class A);而单个
数字(不带点)被解释为 32 位数量并直接存储在网络地址中。更添
混乱的是,某些实现允许每个点分部分按 C 语言规定解释为十进制、
八进制或十六进制(即,前导 0x 或 0X 表示十六进制;前导 0 表示
八进制;否则,该数字解释为十进制)。
由于平台实现之间存在差异,URI 语法中不允许这些附加 IP 地址格式。
然而,如果应用尝试基于字符串字面量格式的 IP 地址来过滤对资源的
访问,它们就可能成为安全关注点。如果执行这种过滤,应将字面量
转换为数值形式,并基于数值进行过滤,而不是基于字符串形式的前缀
或后缀进行过滤。
7.5. 敏感信息
URI 生产者不应提供包含意在保密的用户名或密码的 URI。URI 经常由
浏览器显示,以明文形式存储在书签中,并由用户代理历史和中介应用
(代理)记录。出现在 userinfo 组成部分内的密码已被弃用,除非在
少数 rare 情况下该 'password' 参数意在公开,否则应被视为错误
(或直接忽略)。
7.6. 语义攻击
由于 userinfo 子组成部分很少使用,并且出现在 authority 组成部分
中 host 之前,它可用于构造一个意在误导人类用户的 URI:看起来
像是在标识一个(可信的)命名授权机构,而实际上标识的是隐藏在
噪声之后的另一个 authority。例如
Berners-Lee, et al. 标准轨道 [第 45 页]
RFC 3986 URI 通用语法 2005 年 1 月
ftp://cnn.example.com&story=breaking_news@10.0.0.1/top_story.htm
可能会使人类用户假定 host 是 'cnn.example.com',而实际上它是
'10.0.0.1'。请注意,误导性的 userinfo 子组成部分可能比上面的
示例长得多。
诸如上面所示的误导性 URI,是对用户关于 URI 含义的先入观念的
攻击,而不是对软件本身的攻击。用户代理在呈现 URI 时,通过区分
URI 的各个组成部分,可能能够减轻此类攻击的影响,例如当存在
userinfo 时使用不同颜色或色调来呈现它,尽管没有万能解决办法。
关于基于 URI 的语义攻击的更多信息可见 [Siedzik]。
8. IANA 考虑事项
URI 方案名称,如 第 3.1 节 中 <scheme> 所定义,构成一个由
IANA 按照 [BCP35] 中定义的过程管理的已注册名称空间。本文档
不要求 IANA 采取任何行动。
9. 致谢
本规范源自 RFC 2396 [RFC2396]、RFC 1808
[RFC1808] 和 RFC 1738 [RFC1738];这些文档中的致谢仍然
适用。它还纳入了主机语法中 IPv6 字面量的更新(带有更正),
该更新由 Robert M. Hinden、Brian E. Carpenter 和 Larry Masinter
在 [RFC2732] 中定义。此外,谨此感谢 Gisle Aas、Reese Anschultz、
Daniel Barclay、Tim Bray、Mike Brown、Rob Cameron、Jeremy Carroll、
Dan Connolly、Adam M. Costello、John Cowan、Jason Diamond、Martin
Duerst、Stefan Eissing、Clive D.W. Feather、Al Gilman、Tony Hammond、
Elliotte Harold、Pat Hayes、Henry Holtzman、Ian B. Jacobs、Michael
Kay、John C. Klensin、Graham Klyne、Dan Kohn、Bruce Lilly、Andrew
Main、Dave McAlpin、Ira McDonald、Michael Mealling、Ray Merkert、
Stephen Pollei、Julian Reschke、Tomas Rokicki、Miles Sabin、Kai
Schaetzl、Mark Thomson、Ronald Tschalaer、Norm Walsh、Marc Warne、
Stuart Williams 和 Henry Zongaro 的贡献。
10. 参考文献
10.1. 规范性参考文献
[ASCII] American National Standards Institute,“编码字符集 --
7 位美国信息交换标准代码”,ANSI X3.4,1986。
Berners-Lee, et al. 标准轨道 [第 46 页]
RFC 3986 URI 通用语法 2005 年 1 月
[RFC2234] Crocker, D. and P. Overell,“语法规范的扩展 BNF:
ABNF”,RFC 2234,1997 年 11 月。
[STD63] Yergeau, F.,“UTF-8,ISO 10646 的一种转换格式”,
STD 63,RFC 3629,2003 年 11 月。
[UCS] International Organization for Standardization,
“信息技术 - 通用多八位组编码字符集(UCS)”,
ISO/IEC 10646:2003,2003 年 12 月。
10.2. 资料性参考文献
[BCP19] Freed, N. and J. Postel,“IANA Charset 注册过程”,
BCP 19,RFC 2978,2000 年 10 月。
[BCP35] Petke, R. and I. King,“URL 方案名称注册过程”,
BCP 35,RFC 2717,1999 年 11 月。
[RFC0952] Harrenstien, K., Stahl, M., and E. Feinler,“DoD Internet
host 表规范”,RFC 952,1985 年 10 月。
[RFC1034] Mockapetris, P.,“域名 - 概念与设施”,
STD 13,RFC 1034,1987 年 11 月。
[RFC1123] Braden, R.,“Internet 主机要求 - 应用和支持”,
STD 3,RFC 1123,1989 年 10 月。
[RFC1535] Gavron, E.,“广泛部署的 DNS 软件中的一个安全问题及
建议修正”,RFC 1535,
1993 年 10 月。
[RFC1630] Berners-Lee, T.,“WWW 中的通用资源标识符:用于表达
网络上对象名称和地址的一种统一语法,作为万维网所用”,
RFC 1630,1994 年 6 月。
[RFC1736] Kunze, J.,“Internet 资源定位符的功能性建议”,
RFC 1736,1995 年 2 月。
[RFC1737] Sollins, K. and L. Masinter,“统一资源名称的功能性要求”,
RFC 1737,1994 年 12 月。
[RFC1738] Berners-Lee, T., Masinter, L., and M. McCahill,“统一
资源定位符(URL)”,RFC 1738,1994 年 12 月。
[RFC1808] Fielding, R.,“相对统一资源定位符”,
RFC 1808,1995 年 6 月。
Berners-Lee, et al. 标准轨道 [第 47 页]
RFC 3986 URI 通用语法 2005 年 1 月
[RFC2046] Freed, N. and N. Borenstein,“多用途 Internet 邮件
扩展(MIME)第二部分:媒体类型”,RFC 2046,
1996 年 11 月。
[RFC2141] Moats, R.,“URN 语法”,RFC 2141,1997 年 5 月。
[RFC2396] Berners-Lee, T., Fielding, R., and L. Masinter,“统一
资源标识符(URI):通用语法”,RFC 2396,
1998 年 8 月。
[RFC2518] Goland, Y., Whitehead, E., Faizi, A., Carter, S., and D.
Jensen,“用于分布式创作的 HTTP 扩展 --
WEBDAV”,RFC 2518,1999 年 2 月。
[RFC2557] Palme, J., Hopmann, A., and N. Shelness,“聚合文档的
MIME 封装,例如 HTML(MHTML)”,RFC 2557,
1999 年 3 月。
[RFC2718] Masinter, L., Alvestrand, H., Zigmond, D., and R. Petke,
“新 URL 方案指南”,RFC 2718,1999 年 11 月。
[RFC2732] Hinden, R., Carpenter, B., and L. Masinter,“URL 中
字面量 IPv6 地址的格式”,RFC 2732,1999 年 12 月。
[RFC3305] Mealling, M. and R. Denenberg,“联合 W3C/IETF URI 规划
兴趣小组报告:统一资源标识符(URI)、URL 和统一资源名称
(URN):澄清与建议”,RFC 3305,
2002 年 8 月。
[RFC3490] Faltstrom, P., Hoffman, P., and A. Costello,
“应用中的国际化域名(IDNA)”,
RFC 3490,2003 年 3 月。
[RFC3513] Hinden, R. and S. Deering,“Internet 协议版本 6
(IPv6)寻址体系结构”,RFC 3513,2003 年 4 月。
[Siedzik] Siedzik, R.,“语义攻击:URL 中有什么?”,
2001 年 4 月,<http://www.giac.org/practical/gsec/
Richard_Siedzik_GSEC.pdf>.
Berners-Lee, et al. 标准轨道 [第 48 页]
RFC 3986 URI 通用语法 2005 年 1 月
附录 A. URI 的汇总 ABNF
URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
hier-part = "//" authority path-abempty
/ path-absolute
/ path-rootless
/ path-empty
URI-reference = URI / relative-ref
absolute-URI = scheme ":" hier-part [ "?" query ]
relative-ref = relative-part [ "?" query ] [ "#" fragment ]
relative-part = "//" authority path-abempty
/ path-absolute
/ path-noscheme
/ path-empty
scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
authority = [ userinfo "@" ] host [ ":" port ]
userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
host = IP-literal / IPv4address / reg-name
port = *DIGIT
IP-literal = "[" ( IPv6address / IPvFuture ) "]"
IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
IPv6address = 6( h16 ":" ) ls32
/ "::" 5( h16 ":" ) ls32
/ [ h16 ] "::" 4( h16 ":" ) ls32
/ [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
/ [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
/ [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32
/ [ *4( h16 ":" ) h16 ] "::" ls32
/ [ *5( h16 ":" ) h16 ] "::" h16
/ [ *6( h16 ":" ) h16 ] "::"
h16 = 1*4HEXDIG
ls32 = ( h16 ":" h16 ) / IPv4address
IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
Berners-Lee, et al. 标准轨道 [第 49 页]
RFC 3986 URI 通用语法 2005 年 1 月
dec-octet = DIGIT ; 0-9
/ %x31-39 DIGIT ; 10-99
/ "1" 2DIGIT ; 100-199
/ "2" %x30-34 DIGIT ; 200-249
/ "25" %x30-35 ; 250-255
reg-name = *( unreserved / pct-encoded / sub-delims )
path = path-abempty ; 以 “/” 开始或为空
/ path-absolute ; 以 “/” 开始但不是 “//”
/ path-noscheme ; 以非冒号段开始
/ path-rootless ; 以一个段开始
/ path-empty ; 零个字符
path-abempty = *( "/" segment )
path-absolute = "/" [ segment-nz *( "/" segment ) ]
path-noscheme = segment-nz-nc *( "/" segment )
path-rootless = segment-nz *( "/" segment )
path-empty = 0<pchar>
segment = *pchar
segment-nz = 1*pchar
segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" )
; 不含任何冒号 “:” 的非零长度段
pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
query = *( pchar / "/" / "?" )
fragment = *( pchar / "/" / "?" )
pct-encoded = "%" HEXDIG HEXDIG
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
reserved = gen-delims / sub-delims
gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
/ "*" / "+" / "," / ";" / "="
附录 B. 使用正则表达式解析 URI 引用
由于 “first-match-wins” 算法与 POSIX 正则表达式所使用的“贪婪”
消歧方法相同,因此使用正则表达式来解析 URI 引用潜在的五个
组成部分,是自然且常见的做法。
以下行是用于将格式良好的 URI 引用拆分为其组成部分的正则表达式。
Berners-Lee, et al. 标准轨道 [第 50 页]
RFC 3986 URI 通用语法 2005 年 1 月
^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
12 3 4 5 6 7 8 9
上面第二行中的数字仅用于辅助可读性;它们表示每个子表达式
(即每对括号)的引用点。我们将子表达式 <n> 所匹配的值称为
$<n>。例如,将上面的表达式匹配到
http://www.ics.uci.edu/pub/ietf/uri/#Related
会得到以下子表达式匹配:
$1 = http:
$2 = http
$3 = //www.ics.uci.edu
$4 = www.ics.uci.edu
$5 = /pub/ietf/uri/
$6 = <undefined>
$7 = <undefined>
$8 = #Related
$9 = Related
其中 <undefined> 表示该组成部分不存在,如上例中的 query
组成部分。因此,我们可以确定五个组成部分的值为
scheme = $2
authority = $4
path = $5
query = $7
fragment = $9
反过来,我们可以使用 第 5.3 节 的算法,从各组成部分重新创建
URI 引用。
附录 C. 在上下文中界定 URI
URI 经常通过不能为其解释提供清晰上下文的格式传输。例如,URI
有许多场合会被包含在纯文本中;示例包括电子邮件、USENET 新闻
以及印刷纸张中发送的文本。在这类情况下,能够将 URI 与文本其余
部分界定开来很重要,尤其要将其与可能被误认为 URI 一部分的标点
符号区分开。
在实践中,URI 会以多种方式界定,但通常位于双引号
"http://example.com/"、尖括号 <http://example.com/> 内,或者仅
使用空白:
Berners-Lee, et al. 标准轨道 [第 51 页]
RFC 3986 URI 通用语法 2005 年 1 月
http://example.com/
这些包裹符不构成 URI 的一部分。
在某些情况下,可能必须添加额外空白(空格、换行、制表符等),
以便将长 URI 跨行断开。提取 URI 时应忽略这些空白。
不应在连字符(“-”)字符之后引入空白。由于某些排版器和打印机
可能会在断行时(错误地)在行尾引入连字符,因此对于包含紧跟在
连字符之后换行的 URI,解释器应忽略换行周围的所有空白,并应意识
到该连字符可能实际是 URI 的一部分,也可能不是。
尤其建议使用 <> 尖括号包围每个 URI,作为包含嵌入空白的引用的
界定样式。
前缀 “URL:”(无论是否带有尾随空格)以前曾被建议用作帮助将 URI
与其他括号内指示符区分开的方式,但它在实践中并不常用,且不再
推荐。
为增强健壮性,接受用户输入 URI 的软件应尝试识别并去除定界符和
嵌入空白。
例如,文本
是的,Jim,我在 "http://www.w3.org/Addressing/" 下找到了它,
但你也许可以从 <ftp://foo.example.
com/rfc/> 获取它。请注意 <http://www.ics.uci.edu/pub/
ietf/uri/historical.html#WARNING> 中的警告。
包含 URI 引用
http://www.w3.org/Addressing/
ftp://foo.example.com/rfc/
http://www.ics.uci.edu/pub/ietf/uri/historical.html#WARNING
Berners-Lee, et al. 标准轨道 [第 52 页]
RFC 3986 URI 通用语法 2005 年 1 月
附录 D. 相对于 RFC 2396 的变更
D.1. 增补
引入了 URI 的 ABNF 规则,以对应该术语的一种常见用法:带有可选
fragment 的绝对 URI。
IPv6(以及后续版本)字面量已被添加到 authority 组成部分中 host
部分的可能标识符列表中,如 [RFC2732] 所述,同时将 “[” 和 “]”
添加到保留集合,并添加版本标志以预期 IP 字面量的未来版本。
方括号现在被指定为 authority 组成部分内的保留字符,并且不允许
在其作为 host 内 IP 字面量定界符的用途之外出现。为了在不改变
path、query 和 fragment 组成部分技术定义的情况下作出这一改变,
这些规则被重新定义为直接指定允许的字符。
由于 [RFC2732] 将 IPv6 字面量地址的定义交由 [RFC3513],而后者
遗憾地缺少 IPv6address 的 ABNF 描述,因此我们创建了新的
IPv6address ABNF 规则,以匹配 [RFC3513] 第 2.2 节 定义的
文本表示。同样,为了将每个十进制八位组限制在 0-255 范围内,
IPv4address 的定义也得到了改进。
关于 URI 规范化和比较的 第 6 节 已使用 Tim Bray 的输入以及
W3C 技术架构组内的讨论结果,进行了完全重写和扩展。
D.2. 修改
RFC 2396 的临时 BNF 语法已替换为 [RFC2234] 的 ABNF。
这一改变要求所有以前包含下划线字符的规则名改为使用短横线命名。
此外,若干语法规则被消除或简化,以使整体语法更容易理解。引用
已废弃语法规则的规范,可以通过按下表替换这些规则来理解:
Berners-Lee, et al. 标准轨道 [第 53 页]
RFC 3986 URI 通用语法 2005 年 1 月
+----------------+--------------------------------------------------+
| 已废弃规则 | 转换 |
+----------------+--------------------------------------------------+
| absoluteURI | absolute-URI |
| relativeURI | relative-part [ "?" query ] |
| hier_part | ( "//" authority path-abempty / |
| | path-absolute ) [ "?" query ] |
| | |
| opaque_part | path-rootless [ "?" query ] |
| net_path | "//" authority path-abempty |
| abs_path | path-absolute |
| rel_path | path-rootless |
| rel_segment | segment-nz-nc |
| reg_name | reg-name |
| server | authority |
| hostport | host [ ":" port ] |
| hostname | reg-name |
| path_segments | path-abempty |
| param | *<pchar excluding ";"> |
| | |
| uric | unreserved / pct-encoded / ";" / "?" / ":" |
| | / "@" / "&" / "=" / "+" / "$" / "," / "/" |
| | |
| uric_no_slash | unreserved / pct-encoded / ";" / "?" / ":" |
| | / "@" / "&" / "=" / "+" / "$" / "," |
| | |
| mark | "-" / "_" / "." / "!" / "~" / "*" / "'" |
| | / "(" / ")" |
| | |
| escaped | pct-encoded |
| hex | HEXDIG |
| alphanum | ALPHA / DIGIT |
+----------------+--------------------------------------------------+
不推荐使用上述已废弃规则来定义特定于方案的语法。
关于字符的 第 2 节 已被重写,以解释哪些字符是保留的、何时
被保留,以及即使它们不被通用语法用作定界符,为什么仍被保留。
通常不安全解码的 mark 字符,包括感叹号(“!”)、星号(“*”)、
单引号(“'”)以及左、右圆括号(“(” 和 “)”),已被移动到保留
集合中,以明确保留字符和非保留字符之间的区别,并希望回答方案
设计者最常见的问题。同样,关于百分号编码字符的章节也已重写,
URI 规范化器现在被许可解码任何对应于非保留字符的百分号编码
Berners-Lee, et al. 标准轨道 [第 54 页]
RFC 3986 URI 通用语法 2005 年 1 月
八位组。一般而言,术语 “escaped” 和 “unescaped” 分别已被
“percent-encoded” 和 “decoded” 替换,以减少与其他形式转义机制的
混淆。
URI 和 URI-reference 的 ABNF 已重新设计,使其对 LALR 解析器更
友好,并降低复杂性。因此,语法描述的布局形式被移除,同时 uric、
uric_no_slash、opaque_part、net_path、abs_path、rel_path、
path_segments、rel_segment 和 mark 规则也被移除。所有对 “opaque”
URI 的引用都已替换为对 path 组成部分如何可能对层次结构不透明的
更好描述。relativeURI 规则已替换为 relative-ref,以避免它们是否
是 URI 子集这一不必要的混淆。关于 URI-reference 解析为 URI,
还是解析为第一段中含冒号的 relative-ref 的歧义,已通过使用五条
单独的 path 匹配规则消除。
fragment identifier 已移回通用语法组成部分一节,并纳入 URI 和
relative-ref 规则中,尽管它仍被排除在 absolute-URI 之外。由于
重新集成 fragment 语法,数字符号(“#”)字符已移回保留集合。
ABNF 已被修正,以允许 path 组成部分为空。这也允许 absolute-URI
在 “scheme:” 之后没有任何内容,这种情况在实践中存在于 “dav:”
名称空间 [RFC2518] 以及许多 WWW 浏览器实现内部使用的 “about:”
方案中。authority 与 path 之间边界的歧义,已通过使用五条单独的
path 匹配规则消除。
使用通用语法的基于注册表的命名授权机构,现在在 host 规则内定义。
这一改变使当前的实现能够与本规范保持一致,在这些实现中,所提供
的任何名称都会被直接送入本地名称解析机制。它还移除了在此重新
规定 DNS 名称格式的需要。此外,它允许 host 组成部分包含百分号
编码八位组,这是使国际化域名能够在 URI 中提供、在 URI 处理之上
的应用层以其原生字符编码处理,并作为 UTF-8 字符编码中的已注册
名称传递给 IDNA 库所必需的。server、hostport、hostname、
domainlabel、toplabel 和 alphanum 规则已被移除。
[RFC2396] 的解析相对引用算法已在本次修订中用伪代码重写,以提
高清晰度并修复以下问题:
Berners-Lee, et al. 标准轨道 [第 55 页]
RFC 3986 URI 通用语法 2005 年 1 月
o [RFC2396] 第 5.2 节 第 6a 步,未能考虑没有 path 的基准 URI。
o 恢复了 [RFC1808] 的行为:如果引用包含空 path 和已定义的
query 组成部分,则目标 URI 继承基准 URI 的 path 组成部分。
o 判断某个 URI 引用是否为 same-document 引用的过程,已从 URI
解析器中解耦,从而以与已部署 URI 处理实现的内部架构一致的
方式,简化了应用内的 URI 处理接口。现在,该判断基于将引用
转换为绝对形式后与基准 URI 的比较,而不是基于引用本身的格式。
这一改变可能导致在本规范下有更多引用被视为 “same-document”,
相比 RFC 2396 所给规则尤其在使用规范化减少别名时更是如此。
然而,它并不改变现有 same-document 引用的状态。
o 将 path 合并例程分离为两个例程:merge,用于描述基准 URI path
与 relative-path 引用的组合;以及 remove_dot_segments,用于
描述如何从组合后的 path 中移除特殊的 “.” 和 “..” 段。
remove_dot_segments 算法现在应用于所有 URI 引用 path,以匹配
常见实现,并改进实践中的 URI 规范化。这一改变只影响异常引用
和同方案引用的解析,其中基准 URI 具有非层次化 path。
索引
A
ABNF 11
绝对 27
absolute-path 26
absolute-URI 27
访问 9
authority 17, 18
B
基准 URI 28
C
字符编码 4
字符 4
字符 8, 11
编码字符集 4
Berners-Lee, et al. 标准轨道 [第 56 页]
RFC 3986 URI 通用语法 2005 年 1 月
D
dec-octet 20
解引用 9
点段 23
F
fragment 16, 24
G
gen-delims 13
通用语法 6
H
h16 20
hier-part 16
层次化 10
host 18
I
标识符 5
IP-literal 19
IPv4 20
IPv4address 19, 20
IPv6 19
IPv6address 19, 20
IPvFuture 19
L
locator 7
ls32 20
M
merge 32
N
name 7
network-path 26
P
path 16, 22, 26
path-abempty 22
path-absolute 22
path-empty 22
path-noscheme 22
path-rootless 22
path-abempty 16, 22, 26
path-absolute 16, 22, 26
path-empty 16, 22, 26
Berners-Lee, et al. 标准轨道 [第 57 页]
RFC 3986 URI 通用语法 2005 年 1 月
path-rootless 16, 22
pchar 23
pct-encoded 12
百分号编码 12
port 22
Q
query 16, 23
R
reg-name 21
已注册名称 20
相对 10, 28
relative-path 26
relative-ref 26
remove_dot_segments 33
表示 9
reserved 12
解析 9, 28
资源 5
检索 9
S
same-document 27
相同性 9
scheme 16, 17
segment 22, 23
segment-nz 23
segment-nz-nc 23
sub-delims 13
后缀 27
T
转录 8
U
统一 4
unreserved 13
URI 语法
absolute-URI 27
ALPHA 11
authority 18
CR 11
dec-octet 20
DIGIT 11
DQUOTE 11
fragment 24
gen-delims 13
Berners-Lee, et al. 标准轨道 [第 58 页]
RFC 3986 URI 通用语法 2005 年 1 月
h16 20
HEXDIG 11
hier-part 16
host 19
IP-literal 19
IPv4address 20
IPv6address 20
IPvFuture 19
LF 11
ls32 20
OCTET 11
path 22
path-abempty 22
path-absolute 22
path-empty 22
path-noscheme 22
path-rootless 22
pchar 23
pct-encoded 12
port 22
query 24
reg-name 21
relative-ref 26
reserved 13
scheme 17
segment 23
segment-nz 23
segment-nz-nc 23
SP 11
sub-delims 13
unreserved 13
URI 16
URI-reference 25
userinfo 18
URI 16
URI-reference 25
URL 7
URN 7
userinfo 18
Berners-Lee, et al. 标准轨道 [第 59 页]
RFC 3986 URI 通用语法 2005 年 1 月
作者地址
Tim Berners-Lee
World Wide Web Consortium
Massachusetts Institute of Technology
77 Massachusetts Avenue
Cambridge, MA 02139
USA
电话: +1-617-253-5702
传真: +1-617-258-5999
EMail: timbl@w3.org
URI: http://www.w3.org/People/Berners-Lee/
Roy T. Fielding
Day Software
5251 California Ave., Suite 110
Irvine, CA 92617
USA
电话: +1-949-679-2960
传真: +1-949-679-2972
EMail: fielding@gbiv.com
URI: http://roy.gbiv.com/
Larry Masinter
Adobe Systems Incorporated
345 Park Ave
San Jose, CA 95110
USA
电话: +1-408-536-3024
EMail: LMM@acm.org
URI: http://larry.masinter.net/
Berners-Lee, et al. 标准轨道 [第 60 页]
RFC 3986 URI 通用语法 2005 年 1 月
完整版权声明
版权所有 (C) The Internet Society (2005)。
本文档受 BCP 78 中所含权利、许可和限制的约束;除其中规定者
外,作者保留其所有权利。
本文档及其中所含信息按“原样”提供,贡献者、其代表或赞助的组织
(如果有)、Internet Society 以及 Internet Engineering Task Force
均不作任何明示或暗示的保证,包括但不限于关于使用本文所含信息
不会侵犯任何权利的保证,或任何关于适销性或特定用途适用性的暗示
保证。
知识产权
IETF 对于可能声称涉及本文档所述技术的实现或使用的任何知识产权
或其他权利的有效性或范围不持立场,也不表示已经作出任何独立努力
来识别此类权利。关于 IETF 文档中权利的 IETF 程序信息,可见
BCP 78 和 BCP 79。
向 IETF 秘书处作出的 IPR 披露副本,以及将提供许可的任何保证,
或实现者或本规范用户为使用此类专有权利而尝试获得一般许可或
允许的结果,可以从 IETF 在线 IPR 资料库获得:
http://www.ietf.org/ipr。
IETF 邀请任何相关方提请其注意任何可能覆盖实现本标准所需技术的
版权、专利或专利申请,或其他专有权利。请将相关信息发送给 IETF:
ietf-ipr@ietf.org。
致谢
RFC Editor 职能的资金目前由 Internet Society 提供。
Berners-Lee, et al. 标准轨道 [第 61 页]