每个 Web 开发者都应该知道的关于 URL 编码的知识 已翻译 100%

oschina 投递于 2013/06/25 08:02 (共 21 段, 翻译完成于 06-25)
阅读 41844
收藏 889
84
加载中

本文首先阐述了人们关于统一资源定位符(URL)编码的普遍的误读,其后通过阐明HTTP场景下的URL encoding 来引出我们经常遇到的问题及其解决方案。本文并不特定于某类编程语言,我们在Java环境下阐释问题,最后从Web应用的多个层次描述如何解决URL编码的问题来结尾。

目录

简介

当我们每天上网冲浪时,有一些技术我们无时无刻不在面对。有数据本身(网页),数据的格式化,能够让我们获取数据的传输机制,以及让Web网络能够真正成为Web的基础及根本:从一页到另一页的链接。这些链接都是URL。

史涛
翻译于 2013/06/25 12:42
11

通用URL语法

我敢说每个人在其一生中至少见过一次URL。比如"http://www.google.com",就是一个URL。一个URL是一个统一资源定位器 ,事实上它指向了一个网页(大多数情况下)。实际上,自从1994年的第一版规范开始,URL就有了一个良好定义的结构。

我们能从"http://www.google.com" 这个URL中读出下列详细信息:

Part Data
Scheme http
Host address www.google.com

如果我们看一个更复杂的URL,比如 "https://bob:bobby@www.lunatech.com:8080/file;p=1?q=2#third" 我们就能获取到下列信息:

Part Data
Scheme https
User bob
Password bobby
Host address www.lunatech.com
Port 8080
Path /file
Path parameters p=1
Query parameters q=2
Fragment third

协议 (即scheme,如上面的httphttps (安全HTTP)) 定义了URL中其余部分的结构。大多数互联网URL协议 拥有通用的开头,包括用户,密码,主机名和端口,后面才是每个协议具体的部分。这个通用的部分负责处理认证,同时它也有能力知道为了请求数据应该链接到哪儿。

lwei
翻译于 2013/06/25 09:37
9

HTTP URL语法

对于HTTP URL (使用httphttps 协议),URL的scheme描述部分定义了数据的路径(path),后面是可选的queryfragment

path 部分看上去是一个分层的结构,类似于文件系统中文件夹和文件的分层结构。path由"/"字符开始,每一个文件夹由"/"分隔,最后是文件。例如"/photos/egypt/cairo/first.jpg"有四个路径片段(segment):"photos"、"egypt"、"cairo" 和 "first.jpg",可以由此推出:"first.jpg" 文件在文件夹"cairo"中,而"egypt" 文件夹位于web站点的根文件夹"photos"里面。

每一个path片段 可以有可选的 path参数 (也叫 matrix参数),这是在path片段的最后由";"开始的一些字符。每个参数名和值由"="字符分隔,像这样:"/file;p=1",这定义了path片段 "file"有一个 path参数 "p",其值为"1"。这些参数并不常用 — 这得清楚 — 但是它们确实是存在,而且从 Yahoo RESTful API 文档我们能找到很好的理由去使用它们:

Matrix参数可以让程序在GET请求中可以获取部分的数据集。参考数据集的分页。因为matrix参数可以跟任何数据集的URI格式的path片段,它们可以在内部的path片段中被使用。

zicode
翻译于 2013/06/25 10:22
10

路径(path)部分之后是 查询 (query)部分,它和 路径 之间由一个“?”隔开, 查询部分包含了一个由“&”分隔开的参数列表,每一个参数由参数名称、“=”号以及参数值组成。比如"/file?q=2"定义了一个 查询参数 "q" ,它的值是"2"。这在提交 HTML表单时,或者当你使用诸如Google搜索等应用时, 用的非常多

一个HTTP URL的最后部分是一个段落(fragment)部分,用来指向HTML文件中具体的某个部分,而不是整个HTML页面。比如说,当你点击链接时浏览器自动滚屏到某个部分而不是从页面最顶部开始展示,就说明你点击了一个拥有段落部分的URL。

lwei
翻译于 2013/06/25 10:14
4

URL 语法

http URL 方案最初由 RFC 1738 定义(实际上,在之前的 RFC 1630也有涉及),而在 http URL 方案被重新定义之前,整个 URL 语法就已经由扩展几次 以适应发展的规范进化为一套 统一资源标识符(Uniform Resource Identifiers 即 URIs)

对于 URLs 如何拼装,各部分如何分隔有一套语法。例如:"://"分隔方案主机部分。主机路径片段部分由"/"分隔,而查询部分紧跟在"?"之后。这意味着有些字符为语法保留。有些为整个URIs保留,而有些则被特定方案保留。所有出现在不应出现位置的 保留符(例如路径片段——以文件名为例——可能包含"?")必须被URL 编码

K6F
K6F
翻译于 2013/06/25 11:01
3

URL 编码将字符转变成对 URL 解析无意义的无害形式。它将字符转化成为一种特定字符编码的字节序列,然后将字节转换为16进制形式,并将其前面加上"%"。问号的 URL 编码形式为"%3F"。

我们可以将指向 "to_be_or_not_to_be?.jpg"图片的 URL 写成:"http://example.com/to_be_or_not_to_be%3F.jpg",这样就没有人会认为这儿可能由一个查询部分了。

现今多数浏览器显示 URLs 前都会对其解码(将百分号编码字节转回其原本字符),并在获取其网络资源的时候重新编码。这样一来,很多用户从未意识到编码的存在。

另一方面,网页作者,开发者必须明确认识到这一点,因为这里存在着很多陷阱。

K6F
K6F
翻译于 2013/06/25 11:25
6

URL常见陷阱

如果你正和URL打交道,了解下能够避免的常见陷阱绝对是值得的。现在我们给大家介绍下不仅限于此的一些常见陷阱。

使用哪类字符编码?

URL编码规范并没有定义使用何种字符编码形式去编码字节。一般的ASCII字母数字字符并不需要转义,但是ASCII之外的保留字需要(例如法语单词“nœud”中的"œ")。我们必须提出疑问,应该使用哪类字符编码来编码URL字节。

当然如果只有Unicode的话,这个世界就会清净很多。因为每个字符都包含其中,但是它只是一个集合,或者说是列表如果你愿意,它本身并不是一中编码。Unicode可以使用多种方式进行编码,譬如UTF-8或者UTF-16(也有其它格式),但是问题并没有解决:我们应该使用哪类字符来编码URL(通常也指URI)。

标准并没有定义一个URI应该以何种方式指定其编码,所以其必须从环境信息中进行推导。对于HTTP URL,它可以是HTML页面的编码格式,或HTTP头的。这通常会让人迷惑,也是许多错误的根源。事实上,最新版的URI标准 定义了新的URI scheme将采用UTF-8,host(甚至已有的scheme)也使用UTF-8,这让我更加怀疑:难道host和path真的可以使用不同的编码方式?

史涛
翻译于 2013/06/25 13:17
6

每一部分的保留字都是不同。

是的,他们是,是的,他们,是的,他们是。。。

对于一个httpd连接,路径片段部分中的空格被编码为"%20"(不,完全没有"+"),而“+”字符在路径片段部分可以保持不编码。

现在,在查询部分,一个空格可能会被编码为“+”(为了向后兼容:不要试图在URI标准去搜索他)或者“%20”,当作为“+”字符(作为个统配符的结果)会被编译为“%2B”。

这意味着“blue+light blue”字串,如果在路径部分或者查询部分,将会有不同的编码。比如得到"http://example.com/blue+light%20blue?blue%2Blight+blue"这样的编码形式,这样我们不需从语法上分析url结构,就可以推导这个url的整个结构是可能

桔子
翻译于 2013/06/25 14:35
3

考虑如下组装URL的Java代码片段

String str = "blue+light blue";
String url = "http://example.com/" + str + "?" + str;

编码URL并不是为了转义保留字而进行的简单字符迭代,我们需要确切的知道哪个URL部份有哪些保留字,而有针对性的进行编码。

这也意味着URL重写过滤器如果不考虑合适的编码细节而对URL直接进行分段转换通常是有问题的。对URL进行编码而不考虑具体的分段规则是不切实际的。

史涛
翻译于 2013/06/25 11:35
3

保留字不是你想象的那样

大多数人不知道"+"在路径部分是被允许的并且特指正号而不是空格。其他类似的有:

  • "?"在查询部分允许不被转义,
  • "/"在查询部分允许不被转义,
  • "="在作为路径参数或者查询参数值以及在路径部分允许不被转义,
  • ":@-._~!$&'()*+,;="等字符在路径部分允许不被转义,
  • "/?:@-._~!$&'()*+,;="等字符在任何段中允许不被转义。

  这样下面的地址虽然看起来有点混乱:"http://example.com/:@-._~!$&'()*+,=;:@-._~!$&'()*+,=:@-._~!$&'()*+,==?/?:@-._~!$'()*+,;=/?:@-._~!$'()*+,;==#/?:@-._~!$&'()*+,;="

按照上面的规则,其实上是一个合法的地址。

不用奇怪,上面路径可以被解析为:

部分

协议
http
主机
example.com
路径
/:@-._~!$&'()*+,=
路径参数名 :@-._~!$&'()*+,
路径参数值 :@-._~!$&'()*+,==
查询参数名 /?:@-._~!$'()* ,;
查询参数值 /?:@-._~!$'()* ,;==
/?:@-._~!$&'()*+,;=
桔子
翻译于 2013/06/25 14:52
3
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
加载中

评论(61)

平林新袖
http://localhost/index?name=史涛%26password=123
如果服务端接收到这样的请求,之后服务端在处理的时候,getParameter的时候会只获取到一个参数
key=史涛
value =史涛%26password=123

像这种请求该如何处理??
袁良锭
袁良锭
想了解一下java中的URL的具体用处了。
果实王
果实王
mark
水牛叔叔
水牛叔叔
大开眼界,第一次知道url的结构和规则是这么复杂的
einsein
einsein
收藏
silence_狂想
silence_狂想

先收藏,太长了,没耐心看完!

hillshills
hillshills
学习。。
liuyzss
liuyzss
好,学习了
changdp
changdp
不错,很棒
jiacai2050
jiacai2050
有点长了
返回顶部
顶部