URL 的语法是由Tim Berners-Lee 制定的,具体内容主要参见RFC 3986 文档;在Web 里的实际运用请参见RFC 17382、26163 以及若干相对次要的标准文档。
& [ r7 u% s/ [. H1 r/ `. \# y
, V( t4 K6 o4 {. u# s' `这些文档都非常详细,这导致URL的解析模式相当复杂,但它们又未能足够精确地描述出在客户端软件里需要怎样实现URL机制才能做到既兼容又准确。此外,各家软件开发商出于各自的考虑,都会稍许偏离这些规范。+ t5 N$ B% R% |! i% S m# w; c
4 ~: q/ A% P s: e D+ x# r
让我们来仔细看一下貌似简单的URL 在实际环境中是如何工作的。
& t* K. ^: r \( B! H- q7 q: x l* q( Y% S2 S2 `2 P% j1 t' ^
& m5 p9 t3 h2 g; S8 w7 V
图2-1 显示的是一个符合规范的绝对(absolute)URL,它包括了访问某特定资源所需要的全部信息,绝对URL 和访问时的状态完全无关。+ R0 L1 M7 G5 z: w; x m/ l8 J
+ A/ |3 w7 e3 C
与之相对应的是省略了部分信息的相对(relative)URL,如../file.php?text=hello+world,它需要根据当前浏览所在上下文环境里的基准URL,才能确定完整的URL 地址。# F) S$ f0 D+ N5 F) T
: g0 n: r+ J8 ~, I9 A$ Y+ y( {9 o) d/ S- q! R
, Z& d4 w" ?1 K% \6 P) R5 d2 A
7 e' F) r7 h% y% u6 n' F' r3 z p4 S
% t3 g0 p( ?) a& a( R/ b( W(1) 协议名称0 ?- c$ \" |8 \* a- h
协议名称由一串不区分大小写的字符串组成,以单个冒号结束,表明获取该资源时需要使用的协议。官方认可的有效URL 协议统一由IANA(Internet Assigned NumbersAuthority,互联网数字分配机构)维护,该机构更广为人知的功能是管理IP 地址空间。IANA 当前的有效协议名包括http:、https: 和ftp: 等几十项,而实际上,常用浏览器和第三方应用往往还支持若干额外的协议,其中一些还会带来安全问题。
% n D" _( [0 Y3 K& G0 h8 _
8 H8 A4 _! Z3 c2 B, X3 L! t% M5 v( d0 j* G, I& R o
(2) 层级URL 的标记符号
6 e* `* F/ c7 d8 r' y& q根据RFC 1738 规定的语法,在授权信息之前,每个层级结构的绝对URL 里都应该包括固定的字符串“//”。按这个规范的意思,如果没有这个字符串,就无法确定URL 后续部分的格式和功能了,只能把它们看成一个含糊的与特定协议相关的值。4 o% R$ x$ f; Q7 z
" Y4 [/ C3 g" ~0 q* ~
(3) 访问资源的身份验证
5 b* @+ k% l+ q# z1 y5 e2 QURL 里身份验证的部分属于可选项。在向服务器端获取数据时,有可能需要在该位置指定一个用户名或密码。但这个抽象的URL 语法本身,与具体的用户名密码等身份验证信息的交换并无实质性关系,身份验证信息的传输是和协议相关的。对那些不支持身份验证的协议,如果在URL 里强行加入这部分信息该做何处理,协议并未做出规定。+ w7 P( @! W2 D) \7 `5 c& d2 H
0 P/ A4 \8 Y0 u9 v
(4) 服务器地址
- F( Z& I3 }, s6 b, N对完整的层级URL 来说,服务器地址部分必须指定一个不区分大小写的域名(例如example.com)、一个IPv4 地址( 例如127.0.0.1) 或在一对方括号里的IPv6 地址([0:0:0:0:0:0:0:1]),用以标识请求资源所处的服务器位置。
6 @& k+ E; i8 M3 S7 O( b6 u: b6 }/ i- s9 U# Y
0 ]0 e1 j0 \& V9 e8 s
(5) 服务器端口1 [0 ]9 N+ X6 W
服务器端口部分是可选的,通常在服务器连接的网络端口并非标准端口时才会用到。基本上浏览器支持的所有协议以及第三方应用都会以TCP 或UDP 作为传输方式,而TCP和UDP 都会依赖于一个16 位端口号来区分运行在一台机器上的不同服务。服务器上每种协议通常都会关联一个默认的服务端口(如HTTP 为80、FTP 为21,等等),但这个默认值可以在URL 里另行改写.7 Y6 J0 F! |4 u' u
: z/ K' }( o( n1 U: n" ~+ X9 i1 q0 g" X
(6) 层级的文件路径
& ~9 j4 V2 B' {URL 的下一个组成部分叫层级文件路径,它非常形象地体现了从服务器获取特定资源的方式,例如/documents/2009/my_diary.txt。规范里也公开表明,这个格式是直接从UNIX 目录语义借用过来的,所以也支持在路径里出现的“/../”和“/./”,而对非绝对路径形式的URL,也会根据这种目录格式,加上基准路径再对应到其相对位置上去.
4 J, V# T. M5 X% r( b2 T+ a& l" ~
(7) 查询字符串
" Y/ C9 R" V: i# K查询字符串(Query String)也是个可选项,用于把一串非层级格式的任意参数传递给由前面路径所对应的资源。例如,以下例子就是一个把用户提供的信息传递给服务器端脚本,用于搜索的常见形式:+ {' x2 D* g% V, ?: S
http://example.com/search.php?query=Hello+world
4 c( h; L2 W* w+ ~7 s J8 `: L
2 ~3 A" G# o- T/ D大多数网站开发者都很熟悉这种搜索字符串的特定格式;它是由浏览器在处理HTML文件里的表单时生成的,格式如下:
z2 C9 [- m# Lname1=value1&name2=value2…
( X% E$ m$ E; a0 h, ?4 T4 _
: [7 s8 j5 Z. d4 L% r但让人意外的是,在URL 的RFC 文档里其实并没有硬性规定要使用这样的格式。0 o* W& h+ K: d" @* [
实际上,规范里是把查询字符串当作一堆含混笼统的数据对待的,关键是接收者最后要怎么用它,所以有别于路径,它无需遵守特定的解析规则。
0 W2 P, N( h/ f. v% w
. l* r C; _6 E0 W) n3 Y7 R(8) 片段ID
' y0 }* b# X' d& y; E( D片段 ID( Fragment ID)的角色和查询字符串有点类似,但用法颇为晦涩,它是用于客户端而非服务器端(实际上,这个值根本就不应该传回给服务器端)的一种可选信息。在RFC 里没有明确规定片段ID 的格式或功能,但暗示性地提到可以用于在返回的文档里定位“子资源”或对文档渲染的方式提供一些帮助性的指导。- p3 K) d& P5 L1 m& U) t* d8 l
在实际运用中,片段ID 在浏览器里只有一个用途:指向HTML 页面里的某个锚点名称,用于页面浏览定位。这套逻辑很简单。如果在URL 里的锚点名称与HTML 页面里设定的锚点标签匹配,文档就会滚动到该定位标签的位置上,方便阅读浏览;否则,就啥动作也没有。
0 H; t* H7 d2 T
T3 ~0 M' k0 z7 q @。
& ]: o; R) C! u; K+ x' v以上片段摘自《WEB之困》第二章。
, Q7 |; B- J1 k& w: P4 }& }, G+ f) w5 w! P3 \0 [
然而在《HTTP权威指南》对此处的描述略有不同。
0 {! ^4 W) b: d" q$ f! ~
$ B1 g8 R& ^! }1 N/ a2 PMost URL schemes base their URL syntax on this nine-part general format:
& |- C& d. l* p" j
' K- w8 e) j; W' j9 y://:@:/;?#% h# w f2 L t
' ~' L. t Q4 V, v+ G' j3 Q6 x
Almost no URLs contain all these components. The three most important parts of a URL are the scheme, the host, and$ }" N7 U @ v. {" v" ^6 f1 ~
the path.+ Y/ K9 a. A/ j) K, ]/ Z5 h9 j# h
- P3 o, r; U4 \) x
; J$ {9 Z# N& L经过查找,我发现此处的params选项在 RFC 3986 被废止了(Page 54)。不再推荐使用。
* N. }6 K" ~4 ?( g; i \" G) p" s( f# G/ L$ H. A
1 r9 l5 r, w# x# c( }. z& V
! ?/ l& O, V4 n& a2 n7 L) o |