[关闭]
@liuhui0803 2016-07-22T01:14:11.000000Z 字数 7306 阅读 2256

URL的发展史:路径、片段、查询、身份验证

互联网 URL


URL从没打算过实现现在的这种用途:让用户以一种近乎神秘的方式确认网站身份。然而我们没能让URN成为标准,也就没法使用这种更实用的命名系统。认为目前的URL系统已经够用了,这种想法就类似于赞美DOS命令行,并认为大部分人都需要学习命令行语法。我们使用窗口化系统的原因在于想要让计算机更易用,被更广泛的用户接受。出于类似原因,也许需要用一种更完善的方式定位网络上的网站。
— Dale Dougherty,1996年

“互联网”可以通过多种方式来理解。方式之一将其想象成通过网络连接在一起的计算机所组成的系统。对于互联网的这种认识早在1969年创建ARPANET时就已存在。其实在HTTP、HTML,或“网页浏览器”诞生前,人们已经可以通过网络进行邮件、文件、聊天等活动了。

1992年,Tim Berners-Lee(TBL)发明了三个东西:HTTP协议、HTML,以及URL,它们塑造出今天我们所熟悉的互联网。他的目标是让“超文本”(Hypertext)更为实用。简单理解的话,超文本实际上是一种在不同文档之间相互建立连接的技术。当时这种技术看起来更像是科幻小说里的万灵药,并催生出了超媒体(Hypermedia)以及其他很多以“超(Hyper)”打头的词汇。

超文本的核心要求在于要能在不同文档之间相互连接。TBL当时认为,这些文档可以用多种格式承载,可通过诸如Gopher以及FTP等协议访问。但他希望能通过一种一致的方式引用使用各种协议编码,托管在互联网上,存在于某台主机里的文件。

在1992年3月举行的首届万维网发布会上,TBL将这种技术称之为“通用文档标识符(UDI,Universal Document Identifier)”。当时为这种标识符考虑过很多不同格式

  1. protocol: aftp host: xxx.yyy.edu path: /pub/doc/README
  2. PR=aftp; H=xx.yy.edu; PA=/pub/doc/README;
  3. PR:aftp/xx.yy.edu/pub/doc/README
  4. /aftp/xx.yy.edu/pub/doc/README)

这篇文档也解释了为什么要对URL中的空格进行编码(%20):

UDI中应避免使用空白字符(White space character):空格不是合法字符。这样做是因为频繁使用无关的空白字符会导致邮件等系统需要折行,或不可避免地缩短列宽,此外还要在转换字符编码方式的过程中,以及在应用程序之间传输文本的过程中对不同形式的空白字符互相转换。

更重要的是,从本质上来说,URL只是一种对结构(Scheme)、域名、端口、凭据,以及路径等内容组合产物进行缩略的方法,而以往在不同通信系统中需要结合上下文情境加以理解。

URL最初于1994年通过RFC正式确立。

  1. scheme:[//[user:password@]host[:port]][/]path[?query][#fragment]

这种系统使得我们能够在超文本中引用不同的系统,但发展到今天,几乎所有内容都是通过HTTP方式提供的,因此可能已经不再那么重要。早在1996年,浏览器就已经可以帮助用户自动插入http://www.(这也让当时在广告中的网址里依然包含这些字段的做法显得十分愚蠢)。

路径

我觉得问题并不在于人们能否了解URL的含意,我只是认为迫使爷爷奶奶辈的用户必须理解UNIX文件系统的各种约定到底是什么,在道义上这是一种很可恶的做法。
— Israel del Rio,1996年

过去50年里用过任何类型计算机的任何用户,对于用来分隔URL中路径部分的斜杠应该已经很熟悉了。这种具备层次结构的文件系统是由MULTICS系统发明的,该系统的创造者将这种做法归功于他在1952年与爱因斯坦进行过一次两小时谈话的结果。

MULTICS使用大于号(>)分隔文件路径的不同组件,例如:

  1. >usr>bin>local>awk

这种做法在逻辑上很完美,但不幸的是发明Unix的那帮人决定使用>代表重定向,并使用正斜杠(/)分隔路径。

最高法院,阅后即焚

错了。现在我觉得你我之间存在明显的分歧。
...
作为个体,我保留为不同用途使用不同标准的权力。我希望这些名称能够更通用,可用于任何具体的解释,也可用于任何具体版本。我希望实现比你的提议更加丰富多彩的世界。我不想受到你那种由“文档”和“变体”组成的两级系统的束缚。
— Tim Berners-Lee,1993年

美国最高法院意见里使用URL指向的页面中,一半页面已经不复存在。如果你在2011年阅读一篇2001年写就的学术论文,你肯定会发现很多已经失效了的URL。

1993年,很多人认为URL会消失,人们会转为使用“URN”。统一资源名称(Uniform Resource Name)是对特定内容的一种永久引用,与URL不同,URN永远不会改动或失效。Tim Berners-Lee早在1991年就首先评论了这一“迫切需求”。

创建URN最简单的办法可能是对页面内容使用算法创建哈希,例如urn:791f0de3cfffc6ec7a0aacda2b147839。但这种方法无法满足网络社区的某些要求,因为无法真正确定向谁提出申请才能将这串哈希转换为实际内容。此外这种方式也没能充分考虑文件经常可能进行的格式变化(例如压缩和解压缩),尽管格式再怎么变内容都是一样的。

URL的可靠性

1996年,Keith Shafer和其他几人针对URL失效问题提议了一个解决方案,介绍该解决方案的链接现已失效。Roy Fielding于1995年7月公布了一套实施建议,该链接也已失效。

不过我们可以通过谷歌找到这些页面,而谷歌在这其中的作用已经类似于今天的URN。URN格式最终于1997年正式确定,但在那之后基本没人使用。这种格式本身的实现也很有趣。每个URN包含两个组件:负责对特定类型URN进行解析的authority,以及authority可以理解的,任何格式文档的ID。例如urn:isbn:0131103628代表一本书,本地isbn解析程序(有可能)可从中提取出代表图书URL的永久链接。

考虑到搜索引擎的强大威力,目前最好用的URN格式也许就是直接以文件形式指向早前的URL。我们可以让搜索引擎索引这些信息,然后提供相应的链接:

  1. <!-- On http://zack.is/history -->
  2. <link rel="past-url" href="http://zackbloom.com/history.html">
  3. <link rel="past-url" href="http://zack.is/history.html">

查询参数

application/x-www-form-urlencoded格式在很多方面都是一种反常的畸形,多年来在实施过程中遇到的意外和妥协产生了对互操作性的一系列必备要求,但这些要求无论如何也不能代表好的设计实践。
WhatWG URL规范

如果对网络有所了解,你肯定已经熟悉查询参数了。这些参数会显示在URL中路径组件之后,并包含了类似?name=zack&state=mi这样的选项。你可能会觉得奇怪查询为什么要像HTML对特殊字符进行编码那样使用连接符号(&)。实际上如果熟悉HTML,你可能曾对URL中的连接符号进行编码,将http://host/?x=1&y=2变成http://host/?x=1&amp;y=2http://host?x=1&#38;y=2(这种特殊的混用情况始终存在)。

你可能还注意到Cookie使用了类似但略有不同的格式:x=1;y=2,但这种格式与HTML的字符编码完全不会冲突。这种做法并非W3C疏忽所致,他们其实早在1995年就鼓励大家在查询参数中支持使用;&

最初URL的这部分内容被限制只能用于搜索“索引”。最早发明互联网是为了向高能物理学家提供一种协作方法(也正是出于这一目的才能获得最初的资金)。这并不是说Tim Berners-Lee不知道自己实际上将发明出一种常规用途的通信工具。多年来他一直没有支持在网页中使用表格,尽管物理学家有这样的需求。

总之这些“物理学家们”需要通过某种方式对信息进行编码和链接,并通过某种方式搜索这些信息。为了实现这些目标,Tim Berners-Lee发明了标签。如果某个页面出现<ISINDEX>,浏览器就会知道这个页面是可以搜索的。浏览器将显示搜索字段,允许用户向服务器发送查询。

这种查询使用了用加号(+)分隔的多个关键字这种格式:

  1. http://cernvm/FIND/?sgml+cms

随着互联网逐渐流行,这个标签很快被滥用来做各种事,例如对输入的数值计算平方根。当时很快有人提议也许这种做法过于具体了,我们真的需要一种通用用途的<input>标签。

该提议最终开始使用加号分隔不同组件,使其看起来更像是一种现代化的GET查询:

  1. http://somehost.somewhere/some/path?x=xxxx+y=yyyy+z=zzzz

这个提议远远超出了“广受欢迎”的程度。有人认为我们需要通过某种方式宣告链接另一端的内容是可以搜索的:

  1. <a HREF="wais://quake.think.com/INFO" INDEX=1>search</a>

Tim Berners-Lee认为我们应该通过某种方式定义强类型查询:

  1. <ISINDEX TYPE="iana:/www/classes/query/personalinfo">

我可以有些自信地说:现在回想起来,幸亏当时那种更为通用的解决方案未被采用。

基于古老的SGML类型开始实现<INPUT>的工作始于1993年1月。当时他们(也许有些遗憾地)决定<SELECT>输入需要独立的,更丰富的结构:

  1. <select name=FIELDNAME type=CHOICETYPE [value=VALUE] [help=HELPUDI]>
  2. <choice>item 1
  3. <choice>item 2
  4. <choice>item 3
  5. </select>

也许你会好奇,继续使用<li>而非引入全新的<option>元素这种做法是否是慎重考虑后的决定。当然当时这个提议还有很多备选方案。其中之一提出了一种变量置换方式,其作用有些类似于今天的Angular:

  1. <ENTRYBLANK TYPE=int LENGTH=length DEFAULT=default VAR=lval>
  2. Prompt </ENTRYBLANK>
  3. <QUESTION TYPE=float DEFAULT=default VAR=lval> Prompt </QUESTION>
  4. <CHOICE DEFAULT=default VAR=lval>
  5. <ALTERNATIVE VAL=value1> Prompt1
  6. ...
  7. <ALTERNATIVE VAL=valuen> Promptn
  8. </CHOICE>

在这个例子中将根据type定义的类型对输入内容进行检查,并将页面中的VAR值用于URL中的字符串置换,类似这样:

  1. http://eager.io/apps/$appId

还有别的提议使用@而非=分隔查询组件:

  1. name@value+name@(value&value)

最后Marc Andreessen根据Mosaic中的实现情况提出了我们目前使用的方法:

  1. name=value&name=value&name=value

两个月后Mosaic开始支持method=POST表单,“现代化”的HTML表单就此诞生。

当然,最终也是Marc Andreessen的公司Netscape发明了Cookie格式(并使用了不同的分隔号)。令人痛心的是他们的提议眼光太短浅,这也使得Set-Cookie2的引入变得更为困难,并导致产生了一些目前依然挥之不去的基本结构问题。

片段

URL中“#”之后的内容也叫做片段(Fragment)。在最初的规范中URL就已包含片段了,主要可用于链接到所加载页面中的指定位置。举例来说,如果我的网页上有个锚点:

  1. <a name="bio"></a>

就可以链接到这个锚点:

  1. http://zack.is/#bio

这一概念逐渐扩展到每个元素(不仅仅是锚点),并可应用于id属性,而非name

  1. <h1 id="bio">Bio</h1>

Tim Berners-Lee决定使用这种类似于美国邮寄地址的方式实现基于字符的链接(尽管他生在英国)。他的原话是:

至少在美国的传统邮件地址中,使用数字符号(#)代表部门编号或大楼里的房间号是一种很普遍的做法。因此“12 Acacia Av #12”实际上是指“金合欢树大街12号的大楼里,编号为12号的那个房间”。这种用途使用这个符号看起来是非常合乎常理的。那么http://www.example.com/foo#bar实际上就是指“http://www.example.com/foo这个资源中,名为bar的特定视图”。

其实Douglas Englebart发明的最早的超文本系统也会出于相同目的使用“#”字符。这也许是巧合,或者也可能是偶然的“创意借鉴”。

片段是明确不能包含在HTTP请求中的,这意味着只能在浏览器内使用。这一概念在(引入pushState之前)实施客户端导航的过程中体现出巨大的价值。如果要在不实际发送给服务器的情况下将状态信息存储在URL中,片段功能也会显得极为有用。什么意思?一起看看:

小丘和大山,小题和大做

在电子数据交换[原文如此]方面有一种标准就像SGML那样让人不爽,我是指表单和表单的提交。我了解到的就是这些,除了它看起来很像Fortran,只不过没有空格。
— Tim Berners-Lee,1993年

有一种很流行的看法认为,2002年时,互联网标准的主体对于HTTP 1.1和HTML 4.01的最终定案没起到太大帮助,而紧接着HTML 5诞生了。我把这段时期称作XHTML的黑暗时代。然而事实是负责标准化的那帮人其实难以置信得忙,但他们只会做一些无法给人们带来任何价值的工作。

语义网(Semantic Web)就是一个例子。这个计划的设想是创建一种资源描述框架(作者备注:尽量远离一切嘴里喊着要创造某种框架的人),这样就可以用统一的方式表示有关内容的元数据。举例来说,不要为雪佛兰Stingray创建美观漂亮的网页,创建一份描述这款车尺寸、颜色,以及驾驶过程中因为超速接到罚单数量的RDF文档就行了。

当然这本身不是什么糟糕的想法。但因为这种格式是基于XML的,让整个世界实现文档化,或者让浏览器使用这些文档呈现出有意义的信息,这里面又产生了一个先有鸡还是先有蛋的问题。

这种想法也为哲学辩论提供了一个辽阔的舞台。其中最厉害的一场争论持续了至少10年,人们甚至给这个争论起了一个著名的代号:“httpRange-14”。

httpRange-14意在回答“URL到底是什么”这个基础问题。URL是否总是指向文档,或者可以指向其他任何东西?我能让URL指向我的汽车吗?

他们对于这个问题的回答并不能让人满意。但他们反而开始专注于如何以及何时才能使用303重定向将用户从非文档链接指向文档链接,以及什么时候可以使用URL片段(“#”后面的内容)将用户指向链接的数据

按照今天这种务实的想法来看,这似乎是一种挺蠢的问题。对我们大部分人来说,你可以将URL用于任何用途,人们愿不愿用是自己的事。但语义网除了语义本身不关注其他任何东西,所以也就那么回事了。

具体到这个话题,也曾在2002年7月1日2002年7月15日2002年7月22日2002年7月29日2002年9月16日,以及直到2005年的其他至少20个其他场合展开过讨论。最终2005年颁布的“httpRange-14决议”解决了这一争议,后又在2007年2011年因为有人申诉而重新打开,最后有人在2012年呼吁开发一种新的解决方案学院派的Web工作组对这个问题进行过深入的探讨,但也仅仅是探讨罢了。唯有一件事始终没有实现:把大量语义数据放在网上并通过各种类型的URL提供给用户。

身份验证

你可能已经知道,URL中可以包含用户名和密码:

  1. http://zack:shhhhhh@zack.is

浏览器会用Base64对这些身份验证数据进行编码,并作为页头发送出去:

  1. Authentication: Basic emFjazpzaGhoaGho

使用Base64进行编码的唯一原因在于,这样编码后就可以使用页头本不支持的字符,但这种方式对于用户名和密码信息无法提供任何保护。

尤其是在SSL大规模应用之前的互联网时代,这种做法很成问题。任何可以嗅探网络的人都将能轻松地看到你的密码。对此很多人提出了各种备选方案,包括现在和当时都被广泛用作安全协议的Kerberos

尽管有这么多类似的例子,对浏览器开发商(Mosaic)来说,实施难度最低的依然是基本身份验证这一提议。在开发者获得自行构建身份验证系统所需的工具前,这也使得这种方法成为首个,也是最终唯一的一个解决方案。

Web应用程序

在Web应用程序的世界里,“超链接是网络的基础”这种想法听起来可能有些奇怪。这只是一种将不同文档连接在一起的方法,只不过在样式、代码执行、会话、身份验证等方面逐渐得到了增强,最后造就出七十年代那么多研究人员试图打造(但没成功)的全社会共享的计算体验。而从中得到的结论同样适用于当今的任何项目或初创公司:接受度永远是最该关心的问题。如果能让大家都使用,哪怕你的作品并不完善,用户也会帮你将作品塑造成他们需要的样子。最终结论在于:如果完全没人用,那么你的作品无论在技术上多么正确也没啥用。很多人投入数以百万计工时开发的大量工具今天不也是一个用户都没?


如果还没读过,建议你也能读读本文的上篇,其中谈到了域名、协议和端口。

作者Zack Bloom阅读英文原文The History of the URL: Path, Fragment, Query, and Auth

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注