网络工作组 T. Berners-Lee
征求意见:3986 W3C/MIT
STD:66 R. Fielding
更新:1738 Day Software
废止:2732, 2396, 1808 L. Masinter
类别:标准路线 Adobe Systems
2005 年 1 月
统一资源标识符 (URI):通用语法
本备忘录状态
本文档为互联网社区规定了一项互联网标准路线协议,
并请求就改进提出讨论和建议。
有关此协议的标准化阶段
和状态,请参阅当前版本的《互联网
官方协议标准》(STD 1)。本备忘录的分发不受限制。
Copyright Notice
Copyright (C) The Internet Society (2005).
Abstract
A Uniform Resource Identifier (URI) is a compact sequence of
characters that identifies an abstract or physical resource. This
specification defines the generic URI syntax and a process for
resolving URI references that might be in relative form, along with
guidelines and security considerations for the use of URIs on the
Internet. The URI syntax defines a grammar that is a superset of all
valid URIs, allowing an implementation to parse the common components
of a URI reference without knowing the scheme-specific requirements
of every possible identifier. This specification does not define a
generative grammar for URIs; that task is performed by the individual
specifications of each URI scheme.
Berners-Lee 等人 标准路线 [第 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 等人 标准路线 [第 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 等人 标准路线 [第 3 页]
RFC 3986 URI 通用语法 2005 年 1 月
1. 引言
统一资源标识符 (URI) 提供了一种简单且可扩展的
资源标识方式。本 URI 语法
和语义规范源自万维网
全球信息倡议所引入的概念,该倡议使用这些标识符
可追溯至 1990 年,并在《WWW 中的通用资源标识符》
[RFC1630] 中有所描述。该语法旨在满足
《互联网资源定位符的功能建议》
[RFC1736] 和《统一
资源名称的功能要求》[RFC1737] 中提出的建议。
本文档废止 [RFC2396],后者合并了《统一资源
定位符》[RFC1738] 和《相对统一资源定位符》
[RFC1808],以便为所有 URI 定义单一的通用语法。
本文档还废止 [RFC2732],后者引入了 IPv6 地址语法。
本文档排除 RFC 1738 中定义各个 URI 方案具体语法的
部分;这些部分将作为独立
文档更新。新 URI 方案的注册流程
由 [BCP35] 单独定义。新 URI
方案设计者的建议可见 [RFC2718]。相对于 RFC
2396 的所有重要变更均在附录 D中注明。
本规范按照 [BCP19] 中给出的定义使用术语“字符”和“编码字符
集”,并使用“字符编码”一词代替 [BCP19] 所称的
“charset”。
1.1. URI 概述
URI 具有以下特征:
统一性
统一性提供了多项益处。它允许不同类型
的资源标识符在同一上下文中使用,即使
用于访问这些资源的机制可能不同。它
允许在不同类型的资源标识符之间统一解释共同语法
约定的语义。它
允许引入新类型的资源标识符,而不会
干扰现有标识符的使用方式。它
允许标识符在许多不同上下文中重用,
从而使新的应用程序或协议能够利用一个预先
存在的、大型且广泛使用的资源标识符集合。
Berners-Lee 等人 标准路线 [第 4 页]
RFC 3986 URI 通用语法 2005 年 1 月
资源
本规范不限制什么可以成为
资源的范围;相反,“资源”一词以一般意义使用,
指任何可能由 URI 标识的事物。常见示例
包括电子文档、图像、具有一致用途的信息
来源(例如“洛杉矶今天的天气报告”)、服务
(例如 HTTP 到 SMS 网关)以及
其他资源的集合。资源不一定
可通过互联网访问;例如,人、公司以及
图书馆中的装订书籍也可以是资源。同样,
抽象概念也可以是资源,例如数学方程的运算符和
操作数、关系的类型
(例如“父级”或“雇员”),或者数值(例如零、
一和无穷大)。
标识符
标识符包含在其标识
范围内将所标识对象与所有其他事物区分开来所需的信息。
本文对“标识”和“进行标识”的使用
指的就是将一个资源与所有
其他资源区分开来的目的,而不论该目的如何实现
(例如通过名称、地址或上下文)。不应
将这些术语误解为假设标识符定义或包含了
所引用对象的身份,尽管某些标识符
可能确实如此。也不应假定使用 URI 的系统
会访问所标识的资源:在许多情况下,
URI 仅用于指称资源,并无访问这些资源的意图。
同样,被标识的“一个”资源本质上可能并非
单一对象(例如,资源可以是命名集合,或
随时间变化的映射)。
URI 是由字符序列组成的标识符,
该序列与第 3 节中名为 <URI> 的语法规则匹配。它通过单独定义的
可扩展命名方案集合(第 3.1 节)
实现对资源的统一标识。如何
实现、分配或启用该标识,则委托给
各方案规范。
本规范不对
资源的性质、应用程序为何可能需要引用
资源的原因,或哪些系统可能为了
标识资源而使用 URI 作任何限制。本规范不要求
URI 随时间持续标识同一资源,尽管这是
所有 URI 方案的共同目标。不过,本
Berners-Lee 等人 标准路线 [第 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 等人 标准路线 [第 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”方案
[RFC2141] 下的 URI,这些 URI 即使资源不再存在或
不可用,也要求保持全局唯一且持久;
也用于指具有名称属性的任何其他 URI。
单个方案不必仅被分类为
“名称”或“定位符”之一。来自任何给定方案的 URI 实例
可以具有名称、定位符或二者的特征,这通常
取决于命名机构分配
标识符时的持久性和谨慎程度,而不是
方案本身的任何特性。未来的规范及相关文档应该
使用一般性术语“URI”,而不是更具限制性的术语
“URL”和“URN”[RFC3305]。
Berners-Lee 等人 标准路线 [第 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 等人 标准路线 [第 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 等人 标准路线 [第 9 页]
RFC 3986 URI 通用语法 2005 年 1 月
,可能由资源提供者所作的其他评论或声明
进一步阐明。
尽管许多 URI 方案以协议命名,但这并不
意味着使用这些 URI 会通过该命名协议
访问资源。URI 经常仅仅为了
标识而使用。即使使用 URI 检索资源的表示,
访问也可能通过网关、代理、
缓存以及与方案名称所关联协议无关的名称解析服务
完成。某些 URI 的解析可能需要使用多个协议
(例如,当本地缓存中找不到表示时,
通常同时使用 DNS 和 HTTP 来访问“http”URI 的源服务器)。
1.2.3. 分层标识符
URI 语法按层次组织,其组件从左到右按
重要性递减的顺序排列。对于某些 URI
方案,可见层次仅限于方案本身:
方案组件分隔符(“:”)之后的所有内容都被视为
对 URI 处理不透明。其他 URI 方案则使层次
对通用解析算法显式可见。
通用语法使用斜杠(“/”)、问号(“?”)和
数字符号(“#”)字符来分隔那些
对通用解析器进行标识符分层解释很重要的组件。
除了通过一致使用熟悉语法来提高这类
标识符的可读性外,这种跨命名方案的统一层次表示
还允许相对于该层次建立
与方案无关的引用。
通常,一组或一棵文档“树”会
为共同目的而构建,其中这些文档中绝大多数
URI 引用指向树内资源,而不是
树外资源。类似地,位于特定站点的文档
引用该站点其他资源的可能性远高于
引用远程站点资源。URI 的相对引用
使文档树能够部分独立于其位置
和访问方案。例如,如果文档
使用相对引用互相引用,则同一组超文本文档
可以同时通过“file”、“http”和“ftp”方案访问和遍历。
此外,这类
文档树可以整体移动,而无需更改任何
相对引用。
相对引用(第 4.2 节)通过描述引用
上下文与目标 URI 之间在分层命名空间中的差异来引用资源。
引用解析算法,
Berners-Lee 等人 标准路线 [第 10 页]
RFC 3986 URI 通用语法 2005 年 1 月
在第 5 节中给出,它定义如何将这种引用转换为
目标 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 等人 标准路线 [第 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 包含由
“reserved”集合中的字符分隔的组件和子组件。这些字符之所以称为
“保留”,是因为它们可能(也可能不会)被
通用语法、各方案特定语法或 URI 解引用算法的
实现特定语法定义为分隔符。
如果 URI 组件的数据会与保留字符作为分隔符的用途
发生冲突,那么在形成 URI 之前必须对冲突的数据进行
百分号编码。
Berners-Lee 等人 标准路线 [第 12 页]
RFC 3986 URI 通用语法 2005 年 1 月
reserved = gen-delims / sub-delims
gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
/ "*" / "+" / "," / ";" / "="
保留字符的用途是提供一组
能够与 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 等人 标准路线 [第 13 页]
RFC 3986 URI 通用语法 2005 年 1 月
如果两个 URI 的差异仅在于将一个非保留字符替换为
其对应的百分号编码 US-ASCII 八位字节,则它们等价:它们
标识同一资源。不过,URI 比较实现
并不总是在比较前执行规范化(见第
6 节)。为保持一致性,ALPHA
(%41-%5A 和 %61-%7A)、DIGIT(%30-%39)、连字符(%2D)、句点(%2E)、
下划线(%5F)或波浪号(%7E)范围内的百分号编码八位字节不应该由 URI
生成器创建,并且当在 URI 中发现它们时,URI 规范化器应该将其解码为
对应的非保留字符。
2.4. 何时编码或解码
在正常情况下,URI 内八位字节进行
百分号编码的唯一时机是在从
各组成部分生成 URI 的过程中。此时,实现确定哪些
保留字符将用作子组件分隔符,
哪些字符可以安全地用作数据。一旦生成,URI 始终
处于百分号编码形式。
当 URI 被解引用时,与方案特定解引用过程
有关的组件和子组件(如果有)
必须在安全解码这些组件内的百分号编码八位字节之前
被解析并分离,否则数据可能会被
误认为组件分隔符。唯一的例外是
对应于非保留集合字符的百分号编码八位字节,
它们可以随时解码。例如,与波浪号(“~”)
字符对应的八位字节经常被旧版 URI 处理实现编码为“%7E”;
可以将“%7E”替换为
“~”,而不会改变其解释。
由于百分号(“%”)字符用作
百分号编码八位字节的指示符,因此如果该八位字节要在 URI 中作为数据使用,
必须将它百分号编码为“%25”。实现不得
对同一字符串进行多次百分号编码或解码,因为对
已经解码的字符串再次解码可能导致将百分号
数据八位字节误解释为百分号编码的开始,或者在
对已经百分号编码的字符串再次编码时发生相反情况。
2.5. 标识数据
URI 字符为每个 URI
组件提供标识数据,作为系统之间进行标识的外部接口。
尽管 URI
生成接口的存在和性质对使用其 URI 的客户端隐藏(因此
超出了本规范定义的互操作性要求范围),
但它经常是解释 URI 字符问题时产生混淆和错误的
来源。实现者必须
意识到 URI 的生成和传输涉及多种字符编码:
Berners-Lee 等人 标准路线 [第 14 页]
RFC 3986 URI 通用语法 2005 年 1 月
本地名称和数据编码、
公共接口编码、URI 字符编码、数据格式
编码以及协议编码。
本地名称(例如文件系统名称)使用本地
字符编码进行存储。URI 生成应用程序(例如源
服务器)通常会以本地编码作为
生成有意义名称的基础。URI 生成器会将
本地编码转换为适合公共接口的编码,
然后将公共接口编码转换为受限的
URI 字符集合(保留字符、非保留字符以及百分号编码)。
这些字符随后又被编码为八位字节,
作为数据格式(例如文档字符集)内的引用使用,而这类
数据格式之后通常还会被编码,以便通过
互联网协议传输。
对大多数系统而言,URI
组件中出现的非保留字符被解释为表示
该字符在 US-ASCII 中编码所对应的数据八位字节。URI 使用者假定
字母“X”对应八位字节“01011000”,即使
该假设不正确,这样做也不会造成危害。内部
以不同字符编码形式提供标识符的系统,
例如 EBCDIC,通常会在
内部接口将文本标识符进行字符转换,转换为 UTF-8 [STD63](或
US-ASCII 字符编码的其他某个超集),从而提供比简单地对原始
八位字节进行百分号编码所得结果更有意义的标识符。
例如,考虑一个信息服务,它通过 HTTP 服务器向互联网客户端
提供使用基于 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 等人 标准路线 [第 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 语法由一个分层的
组件序列构成,这些组件称为 scheme、authority、path、query 和
fragment。
URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
hier-part = "//" authority path-abempty
/ path-absolute
/ path-rootless
/ path-empty
scheme 和 path 组件是必需的,不过 path 可以
为空(零个字符)。当 authority 存在时,path 必须
为空或者以斜杠(“/”)字符开头。当
authority 不存在时,path 不能以两个斜杠
字符(“//”)开头。这些限制产生了五条不同的 path
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 等人 标准路线 [第 16 页]
RFC 3986 URI 通用语法 2005 年 1 月
3.1. 方案
每个 URI 都以方案名称开头,该名称引用
在该方案中分配标识符的规范。因此,URI 语法是
一种联合且可扩展的命名系统,其中每个方案的
规范可以进一步限制
使用该方案的标识符语法和语义。
方案名称由字符序列组成,以
字母开头,后跟字母、数字、加号
(“+”)、句点(“.”)或连字符(“-”)的任意组合。虽然方案名称不区分
大小写,但规范形式为小写,并且规定方案的文档
必须使用小写字母。为提高健壮性,实现
应该接受方案名称中的大写字母,将其视为与小写字母等价
(例如既允许“HTTP”也允许“http”),但为了
保持一致性,只应该生成小写方案名称。
scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
本文档不规定各个具体方案。新 URI 方案的
注册过程由 [BCP35] 单独定义。
方案注册表维护方案名称与
其规范之间的映射。新 URI 方案设计者的建议可见
[RFC2718]。URI 方案规范必须定义自己的
语法,使所有匹配其方案特定语法的字符串
也都匹配 <absolute-URI> 语法,如第 4.3 节所述。
当遇到违反一个或多个方案特定
限制的 URI 时,方案特定解析过程应该将该
引用标记为错误,而不是忽略未使用部分;这样做
可以减少等价 URI 的数量,并有助于发现
对通用语法的滥用,这可能表明 URI 是为了
误导用户而构造的(第 7.6 节)。
3.2. 权威部分
许多 URI 方案包含用于命名
权威机构的分层元素,使 URI 其余部分所定义的
命名空间的管理权委托给该权威机构(该机构还可以
进一步委托)。通用语法提供了一种通用
方法,用于区分基于注册名称或
服务器地址的权威部分,以及可选的端口和用户信息。
authority 组件前面是双斜杠(“//”),其终止位置为
下一个斜杠(“/”)、问号(“?”)或数字
符号(“#”)字符,或者 URI 的末尾。
Berners-Lee 等人 标准路线 [第 17 页]
RFC 3986 URI 通用语法 2005 年 1 月
authority = [ userinfo "@" ] host [ ":" port ]
如果 port 组件为空,URI 生成器和规范化器应该省略
分隔 host 和 port 的“:”分隔符。某些
方案不允许 userinfo 和/或 port 子组件。
如果 URI 包含 authority 组件,则 path 组件
必须为空或者以斜杠(“/”)字符开头。非
验证型解析器(即仅将 URI 引用分隔为
主要组件的解析器)通常会忽略 authority 的子组件结构,
将其从双斜杠到
第一个终止分隔符之间视为不透明字符串,直到
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. 主机
authority 的 host 子组件由以下三种形式之一标识:
方括号括起来的 IP 字面量、
点分十进制形式的 IPv4 地址,或注册名称。
host 子组件不区分大小写。URI 中出现 host 子组件并不
意味着该方案要求在
互联网上访问给定主机。在许多情况下,host 语法仅用于
Berners-Lee 等人 标准路线 [第 18 页]
RFC 3986 URI 通用语法 2005 年 1 月
重用为 DNS 创建并部署的现有注册流程,
从而无需部署另一个注册表即可获得
全局唯一名称。不过,这种使用也有其自身
代价:域名所有权可能会随着时间发生变化,其原因可能
并非 URI 生成器所预期。在其他情况下,host 组件中的
数据标识的是与互联网主机毫无关系的注册名称。
我们为 ABNF 规则使用“host”名称,
是因为这是其最常见的用途,而非唯一用途。
host = IP-literal / IPv4address / reg-name
host 的语法规则存在歧义,因为它不能完全
区分 IPv4address 和 reg-name。为
消除语法歧义,我们应用“首次匹配优先”算法:
如果 host 匹配 IPv4address 规则,则应该将其
视为 IPv4 地址字面量,而不是 reg-name。虽然 host
不区分大小写,但为了
统一,生成器和规范化器应该对注册名称和十六进制地址使用小写,
同时仅对百分号编码使用大写字母。
由 Internet Protocol 字面地址(版本 6
[RFC3513] 或更高版本)标识的 host,通过将 IP 字面量
括在方括号(“[”和“]”)中来区分。这是 URI 语法中
唯一允许使用
方括号字符的位置。为了
预留未来尚未定义的 IP 字面地址格式,
实现可以使用可选版本标志来明确指示此类
格式,而不是依赖启发式判断。
IP-literal = "[" ( IPv6address / IPvFuture ) "]"
IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
版本标志并不表示 IP 版本;相反,它
表示字面量格式的未来版本。因此,
实现不得为下面所述的现有
IPv4 和 IPv6 字面地址形式提供版本标志。如果 URI
包含以“v”开头(不区分大小写)的 IP-literal,
表明版本标志存在,而对其进行解引用的
应用程序不了解该版本标志的含义,则
应用程序应该返回适当的“地址
机制不受支持”错误。
由 IPv6 字面地址标识的 host 在
方括号内表示,前面没有版本标志。此处提供的 ABNF
是 [RFC3513] 中 IPv6
字面地址文本定义的转换。此语法不支持
IPv6 作用域寻址区域标识符。
Berners-Lee 等人 标准路线 [第 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 字面地址标识的 host 以
点分十进制表示法表示(由四个范围为 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
由注册名称标识的 host 是字符序列,
通常用于在本地定义的主机或服务
名称注册表中查询,不过 URI 的方案特定语义可能要求
改为使用特定注册表(或固定名称表)。
最常见的名称注册机制是域名系统 (DNS)。
用于在 DNS 中查询的注册名称使用
Berners-Lee 等人 标准路线 [第 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
方案定义为:没有 authority、空 host 和
“localhost”都表示最终用户的计算机,而“http”
方案则认为缺少 authority 或 host 为空是无效的。
本规范不强制规定特定的注册名称
查询技术,因此不会在互操作所需范围之外限制 reg-
name 的语法。相反,它将
注册名称语法一致性问题委托给执行 URI 解析的各应用程序
所在操作系统,由该操作系统决定
为进行 host 标识而允许什么。URI 解析实现可以使用 DNS、
主机表、黄页、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 等人 标准路线 [第 21 页]
RFC 3986 URI 通用语法 2005 年 1 月
3.2.3. 端口
authority 的 port 子组件由 host 后面可选的
十进制端口号指定,并通过一个
冒号(“:”)字符与 host 分隔。
port = *DIGIT
方案可以定义默认端口。例如,“http”方案
定义默认端口“80”,对应其保留的 TCP
端口号。端口号所指定的端口类型(例如
TCP、UDP、SCTP)由 URI 方案定义。如果
port 为空,或者其值与
方案默认端口相同,URI 生成器和规范化器应该省略 port 组件及其“:”分隔符。
3.3. 路径
path 组件包含通常以分层
形式组织的数据,它与非分层 query 组件中的数据
(第 3.4 节)一起,用于在 URI
方案和命名权威机构(如果有)的作用域内标识资源。path 在
第一个问号(“?”)或数字符号(“#”)字符处终止,或者
在 URI 末尾终止。
如果 URI 包含 authority 组件,则 path 组件
必须为空或以斜杠(“/”)字符开头。如果 URI
不包含 authority 组件,则 path 不能以
两个斜杠字符(“//”)开头。此外,URI 引用
(第 4.1 节)可以是相对路径引用,此时
第一个路径段不能包含冒号(“:”)字符。ABNF
需要五条独立规则来消除这些情况的歧义,
对于给定 URI 引用中的 path 子字符串,只有其中一条
会匹配。我们使用通用术语“path component”来描述
解析器与这些规则之一匹配的 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 等人 标准路线 [第 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 为空。
路径段“.”和“..”也称为点段,
被定义用于 path 名称层次中的相对引用。它们
用于相对路径引用
(第 4.2 节)的开头,以指示命名层次
树中的相对位置。这与某些操作系统
文件目录结构中分别表示当前目录
和父目录的作用类似。不过,与文件
系统不同,这些点段仅在 URI path
层次中解释,并在解析过程中被移除(第
5.2 节)。
除分层 path 中的点段外,通用语法将路径段
视为不透明。URI 生成应用程序
经常使用段中允许的保留字符来分隔
方案特定或解引用处理器特定的子组件。例如,
分号(“;”)和等号(“=”)保留字符经常
用于分隔适用于该段的参数和参数值。
逗号(“,”)保留字符也常用于
类似目的。例如,一个 URI 生成器可能使用诸如
“name;v=1.1”的段表示对
“name”1.1 版本的引用,而另一个可能使用诸如“name,1.1”的段
表示相同内容。参数类型可以由方案特定
语义定义,但在大多数情况下,参数语法特定于
URI 解引用算法的实现。
3.4. 查询
query 组件包含非分层数据,它与
path 组件(第 3.3 节)中的数据一起,用于在 URI 的方案和命名权威机构
(如果有)的作用域内标识资源。
query 组件由第一个问号
(“?”)字符指示,并由数字符号(“#”)字符
或 URI 末尾终止。
Berners-Lee 等人 标准路线 [第 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 等人 标准路线 [第 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”语法规则定义的完整引用形式。为了节省
空间并利用分层局部性,许多互联网
协议元素和媒体类型格式允许使用 URI 的缩写形式,
而另一些则将语法限制为特定形式的 URI。
本规范定义最常见的引用语法形式,
因为它们会影响并依赖于
通用语法的设计,需要统一解析算法才能
一致地解释。
4.1. URI 引用
URI-reference 用于表示资源
标识符最常见的使用形式。
URI-reference = URI / relative-ref
Berners-Lee 等人 标准路线 [第 25 页]
RFC 3986 URI 通用语法 2005 年 1 月
URI-reference 要么是 URI,要么是相对引用。如果
URI-reference 的前缀不匹配方案后跟
其冒号分隔符的语法,则该 URI-reference 是相对
引用。
URI-reference 通常首先被解析为五个 URI
组件,以确定存在哪些组件以及
该引用是否为相对引用。然后解析每个组件
的子部分并对其进行验证。URI-reference 的 ABNF
加上“首次匹配优先”的消歧规则,足以
定义通用语法的验证型解析器。熟悉
正则表达式的读者可参阅附录 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 节的引用解析算法获得。
以两个斜杠字符开头的相对引用称为
网络路径引用;这类引用很少使用。以
单个斜杠字符开头的相对引用称为
绝对路径引用。不以斜杠字符开头的相对引用称为
相对路径引用。
包含冒号字符的路径段(例如“this:that”)
不能用作相对路径引用的第一个段,因为
它会被误认为方案名称。此类段必须
在前面加一个点段(例如“https://www.rfc-editor.org/rfc/this:that”),以构成相对
路径引用。
Berners-Lee 等人 标准路线 [第 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 identifier 的语法或用法,因为 fragment 标识
与方案定义正交。不过,鼓励方案规范
包含大量示例,包括
在适当情况下展示该方案 URI 与 fragment identifier
一起使用的示例。
4.4. 同文档引用
当 URI 引用所引用的 URI 除其 fragment
组件(如果有)外,与基础 URI(第 5.1 节)
相同时,该引用称为“同文档”引用。最常见的
同文档引用示例是为空的相对引用,或者仅包含
数字符号(“#”)分隔符及其后的
fragment identifier 的相对引用。
当为了检索
操作而解引用同文档引用时,该引用的目标被定义为与该引用位于同一个
实体(表示、文档或消息)中;
因此,解引用不应该导致新的检索操作。
在比较基础 URI 和目标 URI 之前对其进行规范化,
如第 6.2.2 节和第 6.2.3 节所述,是允许的,但实践中很少
执行。规范化可能扩大同
文档引用的集合,这可能对某些缓存
应用程序有益。因此,引用作者不应该假定某个
略有不同但等价的引用 URI,一定会(或一定不会)
被任何给定应用程序解释为同文档引用。
4.5. 后缀引用
URI 语法被设计用于无歧义地引用资源,并通过
URI 方案实现可扩展性。不过,随着 URI 标识和
使用变得普遍,传统媒体(电视、广播、
报纸、广告牌等)越来越多地使用 URI 的后缀作为
Berners-Lee 等人 标准路线 [第 27 页]
RFC 3986 URI 通用语法 2005 年 1 月
引用,仅由 URI 的 authority 和 path
部分组成,例如
www.w3.org/Addressing/
或者仅使用一个 DNS 注册名称。这类引用
主要供人解释,而不是供机器使用,
假设基于上下文的启发式规则足以
补全 URI(例如,大多数以“www”开头的注册名称
很可能具有“http://”URI 前缀)。虽然没有
一套用于消除 URI 后缀歧义的标准启发式规则,但许多
客户端实现允许用户输入它们并
通过启发式方式解析。
尽管使用后缀引用的做法很常见,
但应尽可能避免,并且在
预期长期引用的情况下绝不应该使用。上述启发式规则
会随着时间变化,特别是当新的 URI 方案
变得流行时,并且脱离上下文使用时经常出错。
此外,它们还可能导致类似
[RFC1535] 所述的安全问题。
由于 URI 后缀与相对路径引用具有相同语法,
因此不能在预期相对
引用的上下文中使用后缀引用。因此,后缀引用仅限于
没有定义基础 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 等人 标准路线 [第 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 等人 标准路线 [第 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 等人 标准路线 [第 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 相同。
--
if ((not strict) and (R.scheme == Base.scheme)) then
undefine(R.scheme);
endif;
Berners-Lee 等人 标准路线 [第 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”例程,用于将
相对路径引用与基础 URI 的 path 合并。按
如下方式完成:
o 如果基础 URI 具有已定义的 authority 组件和空
path,则返回一个由“/”与
引用的 path 连接而成的字符串;否则,
Berners-Lee 等人 标准路线 [第 32 页]
RFC 3986 URI 通用语法 2005 年 1 月
o 返回一个由引用的 path 组件
附加到基础 URI path 除最后一个段外的所有部分之后组成的字符串(即
排除基础 URI
path 中最右侧“/”之后的任何字符;如果基础 URI path
不包含任何“/”字符,则排除整个基础 URI path)。
5.2.4. 移除点段
伪代码还引用“remove_dot_segments”例程,用于
解释并移除所引用 path 中特殊的完整路径段
“.”和“..”。无论 path 是否为相对路径,
都是在从引用中提取 path 后执行此操作,
以便在形成目标 URI 之前移除
任何无效或多余的点段。虽然可以用多种方式完成
这一移除过程,但这里描述一种使用两个字符串
缓冲区的简单方法。
1. 输入缓冲区使用现在已连接的 path
组件初始化,输出缓冲区初始化为空
字符串。
2. 当输入缓冲区非空时,按如下方式循环:
A. 如果输入缓冲区以“../”或“https://www.rfc-editor.org/rfc/”前缀开头,
则从输入缓冲区中移除该前缀;否则,
B. 如果输入缓冲区以“/./”或“/.”前缀开头,
且“.”是完整路径段,则在输入缓冲区中将该
前缀替换为“/”;否则,
C. 如果输入缓冲区以“/../”或“/..”前缀开头,
且“..”是完整路径段,则在输入缓冲区中将该
前缀替换为“/”,并从输出
缓冲区中移除最后一个段及其前面的“/”(如果有);否则,
D. 如果输入缓冲区仅由“.”或“..”组成,则从
输入缓冲区中移除它;否则,
E. 将输入缓冲区中的第一个路径段移动到输出
缓冲区末尾,包括起始的“/”字符(如果
有)以及后续直到但不包括
下一个“/”字符或输入缓冲区末尾的所有字符。
3. 最后,将输出缓冲区作为
remove_dot_segments 的结果返回。
Berners-Lee 等人 标准路线 [第 33 页]
RFC 3986 URI 通用语法 2005 年 1 月
请注意,点段旨在 URI 引用中使用,
以相对于基础 URI 中的名称层次表示标识符。
remove_dot_segments 算法通过移除
多余点段来尊重该层次,而不是将其视为错误或
留给解引用实现进行错误解释。
以下说明如何将上述步骤应用于两个
合并 path 示例,显示每一步之后两个缓冲区的状态。
步骤 输出缓冲区 输入缓冲区
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 等人 标准路线 [第 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 等人 标准路线 [第 35 页]
RFC 3986 URI 通用语法 2005 年 1 月
5.4.1. 正常示例
"g:h" = "g:h"
"g" = "http://a/b/c/g"
"https://www.rfc-editor.org/rfc/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/"
"https://www.rfc-editor.org/rfc/" = "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 解析器都应该能够一致地解析它们。
每个示例都使用与上文相同的基础 URI。
解析器在处理相对路径引用中的“..”段数
多于基础 URI path 中层次级别数的情况时必须小心。
请注意,“..”语法不能
用于更改 URI 的 authority 组件。
"../../../g" = "http://a/g"
"../../../../g" = "http://a/g"
Berners-Lee 等人 标准路线 [第 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"
更不常见的是相对引用使用了不必要
或无意义形式的完整路径段“.”和“..”。
"https://www.rfc-editor.org/rfc/../g" = "http://a/b/g"
"https://www.rfc-editor.org/rfc/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 合并并移除点段之前,
未能将引用的 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 等人 标准路线 [第 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 等人 标准路线 [第 38 页]
RFC 3986 URI 通用语法 2005 年 1 月
6.2. 比较阶梯
实践中使用多种方法测试 URI 等价性。
这些方法形成一个范围,根据
所需处理量和假阴性概率降低程度进行区分。
如上所述,假阴性无法
完全消除。实践中可以降低其概率,但这种
降低需要更多处理,并不适用于所有
应用程序,因为成本可能不合算。
如果将这一系列比较实践看作阶梯,
以下讨论将沿阶梯向上,从
成本低但产生假阴性概率相对较高的实践开始,
再逐步讨论计算
成本更高而假阴性风险更低的实践。
6.2.1. 简单字符串比较
如果两个 URI 作为字符字符串完全相同,
就可以安全地得出它们等价的结论。这种
等价测试的计算成本极低,并且广泛用于
各类应用程序,特别是解析领域。
测试字符串等价性需要一些基本注意事项。
此过程经常称为“逐位”或
“逐字节”比较,这种说法可能会产生误导。测试
字符串是否相等通常基于对组成字符串的
字符逐对比较,从第一个字符开始,
一直进行到两个字符串都耗尽且所有字符都
相等、遇到一对不相等字符,或者
一个字符串先于另一个耗尽。
这种字符比较要求每对字符都处于
可比较形式。例如,如果一个 URI 以 EBCDIC 编码存储在
字节数组中,而另一个存储在 Java String 对象
(UTF-16)中,则直接进行逐位比较会产生
错误。更准确的说法应该是逐字符
相等,而不是逐字节或逐位相等。
实际上,逐字符比较应该在转换为共同字符
编码后按码位逐一执行。
URI 别名的生成和使用会导致假阴性。
无论采用何种比较方法,都可以通过始终以已经
规范化的形式提供 URI 引用来减少不必要的别名
(即与应用下述规范化后
所产生形式相同的形式)。
Berners-Lee 等人 标准路线 [第 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 等人 标准路线 [第 40 页]
RFC 3986 URI 通用语法 2005 年 1 月
6.2.2.3. 路径段规范化
完整路径段“.”和“..”仅用于
相对引用(第 4.1 节)中,并作为
引用解析过程(第 5.2 节)的一部分被移除。不过,一些
已部署实现错误地假设,当引用已经是 URI 时不需要进行引用解析,
因而在非相对 path 中出现点段时未能
将其移除。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”或
最终用户的 host 等价。当方案为 authority 定义默认值,
并希望 URI 引用该默认值时,为了统一、简洁和
国际化,应该将该引用规范化为空 authority。
但是,如果 userinfo 或 port
子组件任一非空,那么即使 host 与默认值相同,也应该显式给出
host。
除非方案
Berners-Lee 等人 标准路线 [第 41 页]
RFC 3986 URI 通用语法 2005 年 1 月
规范允许,否则当关联组件为空时,
规范化不应该移除分隔符。例如,URI“http://example.com/?”
不能假定与上述任何示例等价。同样,
userinfo 子组件中分隔符的存在与否
通常对其解释有意义。fragment 组件
不受任何基于方案的规范化影响;因此,两个仅在
后缀“#”上不同的 URI,无论方案为何都被认为不同。
某些方案定义了由不区分大小写的数据组成的附加子组件,
从而隐式允许规范化器
将这些数据转换为统一大小写(例如全部小写)。
例如,定义 path 某个子组件包含
互联网主机名的 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 等人 标准路线 [第 42 页]
RFC 3986 URI 通用语法 2005 年 1 月
7. 安全考虑
URI 本身并不构成安全威胁。不过,由于 URI
经常被用于提供一组紧凑的网络资源访问
指令,因此必须谨慎正确解释 URI 内的数据,
防止这些数据导致非预期访问,
并避免包含不应以明文
泄露的数据。
7.1. 可靠性和一致性
无法保证某个 URI 一旦用于检索
信息,未来仍能通过该 URI 检索到
相同信息。也无法保证未来通过该 URI
检索到的信息在可观察上与过去
检索到的信息相似。URI 语法并不限制给定
方案或权威机构如何划分其命名空间,或如何随时间
维护它。此类保证只能从
控制该命名空间和相关资源的人获得。特定
URI 方案可以定义其他语义,例如名称持久性,
如果该方案要求所有命名机构都遵守这些语义。
7.2. 恶意构造
有时可以构造 URI,使尝试执行
看似无害的幂等操作,例如
检索表示,实际上会导致可能造成损害的
远程操作。不安全的 URI 通常通过
指定不同于相关网络协议所保留端口号的
端口号来构造。客户端无意间连接到运行
不同协议服务的站点,而 URI 中的数据包含
指令,这些指令按照另一协议进行解释时,
会导致意外操作。此类滥用的常见示例是
使用端口组件为
“25”的基于协议方案,从而欺骗用户代理软件通过 SMTP 服务器发送非预期
或冒充消息。
应用程序应该阻止解引用指定
“知名端口”范围(0 - 1023)内 TCP
端口号的 URI,除非用于解引用该 URI 的协议
与该知名端口预期使用的协议兼容。尽管 IANA 维护
知名端口注册表,但应用程序应该使这种
限制可由用户配置,以避免阻止新服务的
部署。
Berners-Lee 等人 标准路线 [第 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 内数据进行安全检查应该在
解码八位字节之后执行。不过请注意,“%00”百分号编码
(NUL) 可能需要特殊处理,如果
应用程序不期望在组件中接收原始数据,则应该拒绝它。
当 URI path 解释过程
涉及使用后端文件系统或相关系统
函数时,应该格外小心。文件系统通常会赋予
特殊字符(例如“/”、“\”、“:”、“[”和“]”)
以及特殊设备名称(例如“.”、“..”、“...”、“aux”、
“lpt”等)操作含义。在某些情况下,仅仅测试
这类名称是否存在就会导致操作系统暂停或调用无关的
系统调用,从而带来有关
拒绝服务和非预期数据传输的严重安全问题。
本规范不可能列出所有这类重要
字符和设备名称。实现者应该研究
可能连接到其应用程序的存储设备类型的
保留名称和字符,并相应限制
从 URI 组件获得的数据的使用。
Berners-Lee 等人 标准路线 [第 44 页]
RFC 3986 URI 通用语法 2005 年 1 月
7.4. 罕见 IP 地址格式
虽然 IPv4address 的 URI 语法只允许常见的
IPv4 地址字面量点分十进制形式,但许多
处理 URI 的实现使用依赖平台的系统例程,
例如 gethostbyname() 和 inet_aton(),将字符串
字面量转换为实际 IP 地址。不幸的是,这类系统例程
经常允许并处理比
第 3.2.2 节所述更广泛的格式集合。
例如,许多实现允许由三个
数字组成的点分形式,其中最后一部分被解释为 16 位数值
并放入网络地址最右侧两个字节(例如
B 类网络)。类似地,由两个数字组成的点分形式意味着
最后一部分被解释为 24 位数值并放入
网络地址最右侧三个字节(A 类);而
单个数字(不含点)被解释为 32 位数值并
直接存入网络地址。使情况更加
混乱的是,一些实现允许每个点分部分按照 C
语言规定解释为十进制、八进制或十六进制
(即前导 0x 或 0X 表示十六进制;前导 0
表示八进制;否则数字解释为十进制)。
由于平台实现之间存在差异,这些额外 IP 地址格式
不允许出现在 URI 语法中。不过,如果应用程序尝试
根据字符串字面量形式的 IP 地址过滤
对资源的访问,它们可能成为安全问题。
如果执行这种过滤,应将字面量转换为
数值形式,并根据数值进行过滤,而不是根据
字符串形式的前缀或后缀进行过滤。
7.5. 敏感信息
URI 生成器不应该提供包含预期保密的用户名或
密码的 URI。URI 经常
由浏览器显示、以明文书签存储,并由
用户代理历史记录和中间应用程序(代理)记录。
已弃用 userinfo 组件中出现的密码,
除极少数“password”参数旨在公开的情况外,
应将其视为错误(或直接忽略)。
7.6. 语义攻击
由于 userinfo 子组件很少使用,并且在
authority 组件中出现在 host 之前,因此可以用它构造
一个旨在误导人类用户的 URI,使其看起来标识的是某个
(可信的)命名权威机构,而实际上标识的是隐藏在干扰信息后面的另一个
权威机构。例如
Berners-Lee 等人 标准路线 [第 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 时
区分不同组件来降低此类攻击的影响,例如
如果存在 userinfo,则使用不同颜色或色调来
呈现它,尽管没有万全之策。有关
基于 URI 的语义攻击的更多信息可见 [Siedzik]。
8. IANA 考虑
URI 方案名称,如第 3.1 节中的 <scheme> 所定义,构成一个
注册命名空间,由 IANA 按照
[BCP35] 中定义的程序管理。本文档不要求执行任何 IANA
操作。
9. 致谢
本规范源自 RFC 2396 [RFC2396]、RFC 1808
[RFC1808] 和 RFC 1738 [RFC1738];这些
文档中的致谢仍然适用。本规范还纳入了
Robert M. Hinden、Brian E. Carpenter 和 Larry Masinter 在
[RFC2732] 中定义的 host 语法 IPv6 字面量更新(并作了
修正)。此外,还衷心感谢 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] 美国国家标准协会,《编码字符
集 -- 7 位美国信息
交换标准代码》,ANSI X3.4,1986。
Berners-Lee 等人 标准路线 [第 46 页]
RFC 3986 URI 通用语法 2005 年 1 月
[RFC2234] Crocker, D. 和 P. Overell,《用于语法
规范的增强 BNF:ABNF》,RFC 2234,1997 年 11 月。
[STD63] Yergeau, F.,《UTF-8,ISO 10646 的一种转换格式》,
STD 63,RFC 3629,2003 年 11 月。
[UCS] 国际标准化组织,
《信息技术 - 通用多八位字节编码
字符集 (UCS)》,ISO/IEC 10646:2003,2003 年 12 月。
10.2. 资料性参考文献
[BCP19] Freed, N. 和 J. Postel,《IANA 字符集注册
程序》,BCP 19,RFC 2978,2000 年 10 月。
[BCP35] Petke, R. 和 I. King,《URL
方案名称注册程序》,BCP 35,RFC 2717,1999 年 11 月。
[RFC0952] Harrenstien, K.、Stahl, M. 和 E. Feinler,《DoD 互联网
主机表规范》,RFC 952,1985 年 10 月。
[RFC1034] Mockapetris, P.,《域名 - 概念和设施》,
STD 13,RFC 1034,1987 年 11 月。
[RFC1123] Braden, R.,《互联网主机要求 - 应用
与支持》,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.,《互联网
资源定位符的功能建议》,RFC 1736,1995 年 2 月。
[RFC1737] Sollins, K. 和 L. Masinter,《统一
资源名称的功能要求》,RFC 1737,1994 年 12 月。
[RFC1738] Berners-Lee, T.、Masinter, L. 和 M. McCahill,《统一
资源定位符 (URL)》,RFC 1738,1994 年 12 月。
[RFC1808] Fielding, R.,《相对统一资源定位符》,
RFC 1808,1995 年 6 月。
Berners-Lee 等人 标准路线 [第 47 页]
RFC 3986 URI 通用语法 2005 年 1 月
[RFC2046] Freed, N. 和 N. Borenstein,《多用途互联网邮件
扩展 (MIME) 第二部分:媒体类型》,RFC 2046,
1996 年 11 月。
[RFC2141] Moats, R.,《URN 语法》,RFC 2141,1997 年 5 月。
[RFC2396] Berners-Lee, T.、Fielding, R. 和 L. Masinter,《统一
资源标识符 (URI):通用语法》,RFC 2396,
1998 年 8 月。
[RFC2518] Goland, Y.、Whitehead, E.、Faizi, A.、Carter, S. 和 D.
Jensen,《用于分布式创作的 HTTP 扩展 --
WEBDAV》,RFC 2518,1999 年 2 月。
[RFC2557] Palme, J.、Hopmann, A. 和 N. Shelness,《聚合文档(例如 HTML)的 MIME
封装
(MHTML)》,RFC 2557,1999 年 3 月。
[RFC2718] Masinter, L.、Alvestrand, H.、Zigmond, D. 和 R. Petke,
《新 URL 方案指南》,RFC 2718,1999 年 11 月。
[RFC2732] Hinden, R.、Carpenter, B. 和 L. Masinter,《URL 中的
字面 IPv6 地址格式》,RFC 2732,1999 年 12 月。
[RFC3305] Mealling, M. 和 R. Denenberg,《W3C/IETF URI 规划兴趣联合组报告:
统一资源
标识符 (URI)、URL 和统一资源名称
(URN):澄清与建议》,RFC 3305,
2002 年 8 月。
[RFC3490] Faltstrom, P.、Hoffman, P. 和 A. Costello,
《应用程序中的国际化域名 (IDNA)》,
RFC 3490,2003 年 3 月。
[RFC3513] Hinden, R. 和 S. Deering,《互联网协议版本 6
(IPv6) 寻址架构》,RFC 3513,2003 年 4 月。
[Siedzik] Siedzik, R.,《语义攻击:URL 中有什么?》,
2001 年 4 月,<http://www.giac.org/practical/gsec/
Richard_Siedzik_GSEC.pdf>.
Berners-Lee 等人 标准路线 [第 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 等人 标准路线 [第 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 引用
由于“首次匹配优先”算法与 POSIX 正则表达式使用的
“贪婪”
消歧方法相同,因此使用正则表达式解析
URI 引用可能包含的五个组件是自然且常见的做法。
以下一行是用于将
格式正确的 URI 引用拆分为其组件的正则表达式。
Berners-Lee 等人 标准路线 [第 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 引用的组件重新创建 URI 引用。
附录 C. 在上下文中界定 URI
URI 经常通过不能为其解释提供
清晰上下文的格式传输。例如,URI 经常
包含在纯文本中;示例包括通过电子邮件、
USENET 新闻以及印刷纸张发送的文本。在这种情况下,
必须能够将 URI 与文本其余部分区分开来,
尤其是与可能被误认为
URI 一部分的标点符号区分开。
实践中,URI 以多种方式界定,但通常
位于双引号“http://example.com/”、尖括号
<http://example.com/> 中,或者仅使用空白字符:
Berners-Lee 等人 标准路线 [第 51 页]
RFC 3986 URI 通用语法 2005 年 1 月
http://example.com/
这些包围符不构成 URI 的一部分。
在某些情况下,可能必须添加额外空白字符(空格、换行符、制表符等),
以便将很长的 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 等人 标准路线 [第 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 节定义的文本表示相匹配。
同样,IPv4address 的定义也经过改进,以便
将每个十进制八位字节限制在 0-255 范围内。
关于 URI 规范化和比较的第 6 节已经
完全重写并扩展,其中采用了 Tim Bray 的意见以及
W3C 技术架构组中的讨论成果。
D.2. 修改内容
RFC 2396 的临时 BNF 语法已替换为
[RFC2234] 的 ABNF。此变更要求原来
包含下划线字符的所有规则名称改为使用连字符。
此外,还删除或简化了若干语法规则,
以使整体语法更易理解。引用
已废弃语法规则的规范可以根据下表替换这些规则来理解:
Berners-Lee 等人 标准路线 [第 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 等人 标准路线 [第 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 等人 标准路线 [第 55 页]
RFC 3986 URI 通用语法 2005 年 1 月
o [RFC2396] 第 5.2 节的步骤 6a 未考虑基础 URI
没有 path 的情况。
o 恢复 [RFC1808] 的行为:如果引用
包含空 path 和已定义的 query 组件,则目标
URI 继承基础 URI 的 path 组件。
o 判断 URI 引用是否为同文档
引用的过程已与 URI 解析器解耦,以一种与已部署 URI 处理实现的
内部架构一致的方式简化了
应用程序中的 URI 处理接口。现在的判断基于
将引用转换为绝对形式后与
基础 URI 进行比较,而不是根据引用本身的格式。
此变更可能导致本规范下有更多引用被视为
“同文档”引用,相比
RFC 2396 给出的规则尤其如此,特别是在使用规范化减少
别名时。不过,它不会改变现有
同文档引用的状态。
o 将 path 合并例程拆分为两个例程:merge,用于
描述基础 URI path 与相对路径
引用的组合;remove_dot_segments,用于描述如何从组合后的 path 中移除
特殊的“.”和“..”段。
remove_dot_segments 算法现在应用于所有 URI 引用
path,以匹配常见实现并改进
实践中的 URI 规范化。此变更只会影响
异常引用以及基础 URI 具有非分层 path 的同方案引用的
解析。
索引
A
ABNF 11
absolute 27
absolute-path 26
absolute-URI 27
access 9
authority 17, 18
B
base URI 28
C
character encoding 4
character 4
characters 8, 11
coded character set 4
Berners-Lee 等人 标准路线 [第 56 页]
RFC 3986 URI 通用语法 2005 年 1 月
D
dec-octet 20
dereference 9
dot-segments 23
F
fragment 16, 24
G
gen-delims 13
generic syntax 6
H
h16 20
hier-part 16
hierarchical 10
host 18
I
identifier 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 等人 标准路线 [第 57 页]
RFC 3986 URI 通用语法 2005 年 1 月
path-rootless 16, 22
pchar 23
pct-encoded 12
percent-encoding 12
port 22
Q
query 16, 23
R
reg-name 21
registered name 20
relative 10, 28
relative-path 26
relative-ref 26
remove_dot_segments 33
representation 9
reserved 12
resolution 9, 28
resource 5
retrieval 9
S
same-document 27
sameness 9
scheme 16, 17
segment 22, 23
segment-nz 23
segment-nz-nc 23
sub-delims 13
suffix 27
T
transcription 8
U
uniform 4
unreserved 13
URI grammar
absolute-URI 27
ALPHA 11
authority 18
CR 11
dec-octet 20
DIGIT 11
DQUOTE 11
fragment 24
gen-delims 13
Berners-Lee 等人 标准路线 [第 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 等人 标准路线 [第 59 页]
RFC 3986 URI 通用语法 2005 年 1 月
作者地址
Tim Berners-Lee
万维网联盟
麻省理工学院
77 Massachusetts Avenue
Cambridge, MA 02139
美国
电话:+1-617-253-5702
传真:+1-617-258-5999
电子邮件:timbl@w3.org
URI: http://www.w3.org/People/Berners-Lee/
Roy T. Fielding
Day Software
5251 California Ave., Suite 110
Irvine, CA 92617
美国
电话:+1-949-679-2960
传真:+1-949-679-2972
电子邮件:fielding@gbiv.com
URI: http://roy.gbiv.com/
Larry Masinter
Adobe Systems Incorporated
345 Park Ave
San Jose, CA 95110
美国
电话:+1-408-536-3024
电子邮件:LMM@acm.org
URI: http://larry.masinter.net/
Berners-Lee 等人 标准路线 [第 60 页]
RFC 3986 URI 通用语法 2005 年 1 月
Full Copyright Statement
Copyright (C) The Internet Society (2005).
This document is subject to the rights, licenses and restrictions
contained in BCP 78, and except as set forth therein, the authors
retain all their rights.
This document and the information contained herein are provided on an
"AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
知识产权
IETF 对任何可能被声称与本文档中所述技术的实现或使用
有关的知识产权或其他权利的有效性或范围不持任何
立场,也不对依据此类权利的任何许可
是否可获得或可获得到何种程度作出判断;IETF 也不表示其
曾为识别任何此类权利而进行过任何独立调查。有关
IETF 针对 IETF 文档中权利的处理程序,可参阅 BCP 78 和 BCP 79。
向 IETF 秘书处提交的 IPR 披露副本以及任何
将提供许可的保证,或者为获取一项通用许可或授权,
以供本规范的实现者或用户使用
此类专有权利而进行尝试的结果,都可以从 IETF 的在线 IPR 存储库
http://www.ietf.org/ipr 获取。
IETF 邀请任何相关方将其所知的任何
版权、专利或专利申请,或者其他可能涵盖实现
本标准所需技术的专有
权利提请 IETF 注意。请将相关信息发送给 IETF,地址为 ietf-
ipr@ietf.org。
致谢
RFC Editor 功能目前由
Internet Society 提供资金支持。
Berners-Lee 等人 标准路线 [第 61 页]