Elliotte Rusty Harold, 副教授, Polytechnic University
2006 年 5 月 15 日
惟一标识符
用于 blog 和其他连锁提要的 Atom XML 格式要求每个记录都有一个惟一的 ID,该 ID 放在 id 元素中。Atom 要求这个 ID 必须是语法正确的 URI。它还要求,这个 ID 不仅在出现的文档中是惟一的,而且在任何时候、在所有文档、所有服务器上都必须是全局惟一的。这是非常高的要求。但这是必须的,因为 Atom 提要经常被分成片然后被其他网站如 Bloglines 重新发布,通过聚合程序和 Vienna 填充来自其他站点的内容。比如,在这个实际的记录中,ID 值是非常笨拙的 http://www.cafeaulait.org/?tag=http___eclipse.org_aspectj#news2005December21:
清单 1. Atom 条目的例子
<entry>
<title>The Eclipse Project has released AspectJ 5.0.</title>
<content type="xhtml">
<div xmlns="http://www.w3.org/1999/xhtml">
<p>
The Eclipse Project has released
<a href="http://eclipse.org/aspectj">AspectJ</a> 5.0.
AspectJ is a derivative of Java that allows
programmers to write code that applies across multiple classes.
The AspectJ compiler requires Java 1.3, but can generate code for Java
1.1 and later. "This release constitutes a full-upgrade of AspectJ to
support Java 5, while also delivering a large number of quality
improvements that will benefit users running on JDK 1.4 or below. In
addition to the Java 5 related language changes, AspectJ 5 also
supports an @AspectJ style of aspect declaration, greatly enhanced
load-time weaving capabilities, a full reflection API, and tools APIs
for parts of the weaver."
</p>
</div>
</content>
<link href="#news2005December21"/>
<id>http://www.cafeaulait.org/?tag=
http___eclipse.org_aspectj#news2005December21</id>
<updated>2005-12-21T09:00:01-05:00</updated>
</entry>
(实际上,真正的 ID 更长更难看,为了适应本页,需要对其宽度进行裁减。)
其中一开始是提取 Atom 提要的网页 URL http://www.cafeaulait.org/。接下来的查询字符串增加了一个参数 tag。首先要将记录中引用的所有 URL 连接起来,然后用下划线替换全部 URL 保留字符(如冒号),这样就形成了参数的值,从而可以区分同一页面中不同条目。最后,再增加上日期作为片段标识符,以便在包含同一组 URL 的情况下加以区分。尽管这种 ID 又臭又长而且难以阅读,但确实可以保证惟一性。
Atom 并不是要求使用全球惟一 URI 标识符的惟一一种协议。RDF、OWL 和语义 Web 都假设可以将 URI 赋予任何项 —— 不仅仅是网页,还可以包括人、宠物、星球、草履虫、DNA 序列、医疗诊断、日期以及您能够想象的任何东西。要让语义 Web 有效,就必须保证不能用同一个 URI 标识布鲁托(迪斯尼动画中的宠物狗)和冥王星(Pluto)。
您会发现很多上下文中都使用 URI 作为标识符,包括 SAX 特性和属性名、XML 名称空间、RDDL 性质和目标、XML 数字签名算法、SVG 特性字符串,等等。
所有 URL 都是 URI,前面例子中所示的 HTTP URL 完全可以作为标识符。使用 URL 作为标识符的问题在于用户认为能够打开 HTTP URL。即便出现在 Atom 提要文档中的 URL 不是供人类使用的,人们仍然会将其输入到 Web 浏览器中。用 XML 大师 Claude L. Bullard 的不朽名言来说:
对 URN/URI/URL 的所有吹捧都无法避免一个简单的事实,如果有人将 http:// 放到浏览器显示区域的某个地方,系统用蓝色显示它,并且当鼠标移到上面时指针会变成手的形状。人们希望查看某个资源,如果没有找到,人们就会手足无措。人们没有阅读规范来发现为何会受到震惊,他们只是涨红了脸,把手放在脸上。
除了让用户感到讨厌以外,在文档中加入不能解析的 HTTP URL 时,常常因为您认为没有人会注意到的 URL 而导致服务器日志中写满了 404 错误。即使用户没有发现它们,某个断链机器人也会发现它们。因此,建立与 HTTP URL 不同的纯粹用于标识的 URI 方案很有必要。于是就出现了标签 URI。
标签语法
在不使用某种形式的集中式注册系统来避免冲突的情况下定义全球惟一标识符是不可能的。但是这类注册系统只要有一个就行了,而且幸运的是,已经存在一个几乎每个使用计算机的人都参与的注册系统:域名系统。与 XML 名称空间以及 Java 包名称类似,标签 URI 和 DNS 来确保惟一性。每个标签 URI 都包括域名或者电子邮件地址。
域名可能出售、过期、被抢或者盗窃,因此 URI 中还要包含日期。任何时候应该不会有多个人或组织同时拥有给定的域名或电子邮件地址。
最后在 URI 后面添加一个任意字符串,这样一个人在一天之内就可以创建任意数量的标签 URI。下面是少数标签 URI 的例子:
tag:elharo@ibiblio.org,2006:javafaq/slides
tag:elharo@ibiblio.org,2005-12:Elliotte
tag:elharo.com,2006-01-25:ElliotteHarold:presentations:Javapolis2005-12-14
tag:elharo.com,2005:double
用 URI 的术语来说,这些都是不透明的 URI,就是说没有采用 HTTP、HTTPS、文件和 FTP URL 中使用的层次系统。但是它们也有自己明确的结构。具体来说,每个 URI 都由用冒号分隔的三部分组成:
方案:标签实体:特定标识符
方案必须是 tag。非常简单。虽然 URI 方案对大小写不敏感,但是标签 RFC(请参阅参考资料)建议采用小写形式。
特定标识符是可选的。如果包含特定标识符,则可以包含 URI 语法限制范围内许可的任何内容。简言之,也就是说允许包含 ASCII 字母数字和少数标点符号,但是不能有空格、保留字符 [如冒号(:)和反斜杠(/)] 以及非 ASCII 字符。特定标识符没有特殊的含义。可以存储任何有用的东西,但是应该尽量使其富有意义并浅显易懂。也就是说应避免使用“sr_8_xs_ap_i2_xgl14”(一个大型电子商业网站中实际使用的 URL)这样的 URI。标签 RFC 建议用实际的单词组成字符串。
关键是标签实体。这部分保证了惟一性。标签实体是基于域名的。但是因为域名有可能倒手,因此加上了日期成分。比如,实体 macfaq.com,2005 指的是在 2005 年拥有域名 macfaq.com 的个人或组织。如果 2006 年这个域换了主人,那么 macfaq.com,2006 指的就是新的所有者,而以前的所有者仍然使用 macfaq.com,2005。如果这一年中域名又换了人,还可以增加月份和日期,中间用连字符分开。(这是 ISO 8601 定义并被 W3C 采纳的惯用日期格式。)比如, macfaq.com,2005-12-21 指的是 2005 年 12 月 21 日拥有 macfaq.com 的实体。
标签的相等比较
一种观点认为,应该用一般的 URL 解释方法来区分标签:只有逐个字符对应相等时才认为两个标签相同。即使对固定的方案部分 tag 也不执行大小写转换。可以使用百分数编码,但是不能解析。比如,tag:elharo@ibiblio.org,2006:javafaq/slides、TAG:elharo@ibiblio.org,2006:javafaq/slides 和 tag:elharo%40ibiblio.org,2006:javafaq/slides 被看作是三个不同的 URI。
所有年份都是用四位数,日期和月份使用两位数。比如,要创建 2006 年新年这一天的标签 URI,就要用 macfaq.com,2006-01-01 而不是 macfaq.com,06-1-1。日期不一定是创建 URI 的那一天,但通常如此。也可选择过去的某一天,只要当时您拥有这个域。但是不应建立包含未来日期的标签 URI,因为域名或电子邮件地址的所有权有可能意外地发生改变。
虽然可以在标签实体中增加时间成分,但不鼓励这么做, 因为不同时区可能造成重叠和冲突。如果域名真的换了主人,最好在交割前后 48 小时以外分配标签 URI,以免出现所有权的纷争。
并非每个人都有个人域名,但是大部分有电子邮件地址。如果没有域名,或者公司很大,在部门和分支机构中决定 URI 的用户很难,可以使用完整的电子邮件地址代替,比如:tag:elharo@ibiblio.org,2006:javafaq/slides。这种情况下,标签实体就是 2006 年电子邮件地址 elharo@ibiblio.org 的所有者,而不是 2006 年拥有 ibiblio.org 域名的人或组织。
结束语
标签 URI 最终实现了 URI 要达到的目标:标识而不附加要求其本身没有的定位或者行为信息。它们容易创建、易于阅读、能使用现有系统、是开放标准,并且没有任何向后兼容问题。有什么不好的地方?
建议使用 HTTP URL 而不是标签 URI 的惟一情况就是就是准备在 URL 的另一端放上一个页面,无论现在还是将来。HTTP URL 都可以做到。标签 URI 则不能。但是,用作标识符(而不是定位符)的大部分 HTTP URL 在插入浏览器的时候会产生 404 Not Found 错误。如果不想在 URL 的另一端放上页面,则应使用标签 URI 而不是 HTTP URL。