正文
当你看到这个问题时候,会不会眼含泪水, 对这道题目爱的深沉呢。我背了好久的题目终于被人问到了,但是现实总是这么的残酷,我曾经被问到的时候,大概的场景是这样子的:
面试官: 你能具体的聊聊从你输入url到页面展示发生了哪些事情吗?
面试者: 我对这个块东西还是有比较深刻的理解的。
面试官: 我知道你也说不全,知道啥说啥吧。
面试者: 您怎么知道我说不全呢?
面试者: 首先, 浏览器会对输入的url进行解析
然后, 开始了一场灵魂拷问:
为什么需要对URL进行编码,顺便说一下URI和URL
这里是我们需要对问题进行一下简单的拆分:
URI和URL
首先我们先来下个定义:
URL: RFC1738 统一资源定位符( Uniform Resource Locator)
URN: RFC2141 统一资源名称( Uniform Resource Name)
URI: RFC1630 统一资源标识符 ( Uniform Resource Identifier)
其实从这么官方的语言中,我其实根本不理解都做了什么?我就看了看官方的解释
URL: 俗称网址,是因特网上标准的资源的地址。期望提供可以查找到资源的方法
URN: 期望为资源提供持久的、位置无关的标识方式。并允许简单地将多个命名空间映射到单个URN命名空间
URI: 用于区分资源, 是 URL 和 URN 的超集, 用于取代 URL 和 URN 的概念
URI 或者 URL 你可以具体说一下
在这里我会先对URI
进行一下解释, 然后会分步去解析每一部分。
Resource 资源
-
Resource
可以是图片,文档,资讯,也可以是不能通过网络访问的实体,还可以是一些抽象的概念。 -
一个资源可以有多个
URI
Identifier 标识符
标识符
就是用来将当前的资源和其他的资源区分开的名称
Uniform 统一
- 允许不同的种类的资源在同一上下文中出现
- 对不同种类的资源标识符可以使用同一种语义进行解读
- 引入新的标识符时,不会对已有的标识符造成影响
- 允许同一资源标识符在不同的
Internet
规模下的上下文中出现
好了,你来说一下 URI 的组成部分。
URI 的组成部分
scheme: 方案名或协议名, 表示资源应该用哪种方式来访问
://: 在 scheme
之后,必须是三个特定的字符://
user:passwd@: 身份信息
host:port: 主机名和端口号
path: 标记资源所在的位置
query: URI的查询参数
fragment: 片段标识符
似乎,有点意思了,但是我可能太详细的也不知道了啊。
你能不能把相关了解到的说一说?
scheme: 最常见的就是http
,表示使用HTTP
协议。还有https
,表示经过加密、安全的HTTPS
协议。还有一些不常见的协议ftp
、news
、file
等。当浏览器看到URI
中scheme
的时候,就会去按照对应的协议进行之后的解析,如果URI
连scheme
都没有,其实就不会后续一系列工作。
://: 这三个字符的作用就是为了把 scheme 和 后面的部分分隔开。可能由于历史原因,这个设计也只能被我们所接受。
user:passwd@: 最初的设计是为了登陆主机时的发送用户名和密码,但是现在已经不推荐这种方式, 因为以明文的方式暴露出来,有严重的安全隐患。
host:port: 主机名可以是IP地址
或域名
,但是必须要有,否则浏览器就会找不到服务器。端口是可以省略的,浏览器等客户端会依据scheme
使用默认的端口号, 列如:HTTP
默认80
, HTTPS
默认443
。
path: 这里是采用了类似文件系统目录 路径
的方式,原因是在早期的互联网用多的是UNIX
系统,所以就采用 UNIX
的 /
的风格。
query: 用一个?
开始但是不包含?
,表示对资源附加的额外的要求。这似乎是一个很形象的比喻,很明显的表达了查询
的动作。当然,对于query
会有自己的一套格式,是多个
key=value
的字符串,然后用&
连接。这样做的目的就是为了让浏览器和服务器可以按照这个格式去把查询参数解析成字典
或者数组
。
fragment: 它是一个锚点
或者标签
用来定位URI
的内部资源,浏览器可以在获取资源之后直接跳转到所指向的位置。我个人觉得这个设计很妙,但是呢,fragment
仅可以被浏览器所使用,服务端是看不到的。
那就先聊到这里,你还没回到另一个问题呢?下面我们来回答另外一个问题。
为什么要对ULR进行编码呢?
首先,我们需要知道的是在URI
中只能用ASCII
码,当然可以用其他方式可能会带来有更多的问题,这里我们不做深究。
- 假设在传输数据的过程中,存在用作分隔符的保留字符怎么办?
举个例子🌰:
https://www.baidu.com/s?wd=?#!
不可以正常解析
?
分隔符; #
fragment的分隔符
https://www.baidu.com/s?wd= 掘 金
可以正常解析
https://www.baidu.com/s?wd= 掘'> 金
不可以正常解析
-
对可能歧义性的数据编码
-
不在ASCII范围内的编码
-
ASCII 中不可显示的字符
-
URI 中规定的保留字符
-
不安全的字符() 如空格、引号、尖括号
-
URI 百分号编码方式
- pct-encoded = "%" HEXDIG HEXDIG
- 对于 HEXDIG 十六进制中的字母,大小写等价
其实, URI
的转义规则有点简单粗暴
,直接把非ASCII码
和特殊字符
转换为十六进制字节值,然后前面还加了一个%
.
-
非 ASCII 码字符(例如中文):建议先 UTF8 编码,再 US-ASCII 编码
-
对 URI 合法字符,编码与不编码是等价的
下面会对一些比较难理解的部分做一些解析:
ASCII:
-
128 个字符(95 个可显示字符,33 个不可显示字符)
保留字符:
reserved = gen-delims / sub-delims
gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
gen-delims
字符集用来表示URI component
之间的分隔符,考虑到component
会有不同的subcomponents
组成,因此还需要sub-delims
来定义subcomponents
之间的分隔符。
非保留字符:
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
ALPHA = a-z / A-Z
DIGIT = 0-9
可能还需要大家去学习一下 ABNF
链接
如何对URI进行编码?
我了解到历史上有三种,解码和编码的方式:
-
escape / unescape
-
encodeURI / decodeURI
-
encodeURIComponent / decodeURIComponent
因为 escape已经在标准中删除所以就不做介绍了。
encodeURI()这里我主要会看一下特殊的情况:
encodeURI
会替换所有的字符,但不包括以下字符,即使它们具有适当的UTF-8转义序列
encodeURI
自身无法产生 能适用于HTTP GET
或POST
请求的URI
- 对于
XMLHTTPReuests
, 因为"&"
,"+"
, 和"="
不会被编码,然而在 GET 和 POST 请求中它们是特殊字符。然而encodeURIComponent
可以。
encodeURIComponent 的特殊情况:
-
encodeURIComponent
转义除了如下所示外的所有字符
不转义的字符:
A-Z a-z 0-9 - _ . ! ~ * ' ( )
-
对于
application/x-www-form-urlencoded
(POST) 这种数据方式, 空格需要被替换成 '+', 通常使用encodeURIComponent
的时候还会把 "%20" 替换为 "+"。
大家可以参照下面的代码去实际的体会一下 两者的不同之处:
var set1 = ";,/?:@&=+$"; // 保留字符
var set2 = "-_.!~*'()"; // 不转义字符
var set3 = "#"; // 数字标志
var set4 = "ABC abc 123"; // 字母数字字符和空格
console.log(encodeURI(set1)); // ;,/?:@&=+$
console.log(encodeURI(set2)); // -_.!~*'()
console.log(encodeURI(set3)); // #
console.log(encodeURI(set4)); // ABC%20abc%20123 (the space gets encoded as %20)
console.log(encodeURIComponent(set1)); // %3B%2C%2F%3F%3A%40%26%3D%2B%24
console.log(encodeURIComponent(set2)); // -_.!~*'()
console.log(encodeURIComponent(set3)); // %23
console.log(encodeURIComponent(set4)); // ABC%20abc%20123 (the space gets encoded as %20)
可能这道题刚开始,我都有点想放弃的感觉的了;哈哈,或许这就是知识的魅力吧,之后会陆续更新,有问题我们可以一起交流。
最后还会留几个问题,希望可以和大家一起去寻找答案:
- 你了解 相对URI 吗?
- 你了解到 URI 有几种表现形式?
参考链接: