今天,彻底弄懂什么是URI

3,840 阅读9分钟

又到了每周二的分享时间了,今天分享一下在网络协议中,URI的相关内容,因为在外面出差,头图没法做,就用以前的老图咯~(偷懒也这么理直气壮)

URL是什么我想大家都知道,毕竟我们每天都接触着,那URI是什么呢?

那我们先来看看,如果这世界上没有URI时,会变成什么样子呢?

没有URI的情况下,我上传了一些资料要分享给你们,你们要怎样才能下载获取呢?

首先,我得告诉你们用FTP协议访问naonao.com,端口是8090

然后,告诉你们登陆用户名是Naonao,密码是Handsome

登陆成功后,你们要进入到/Naonao/Source目录下,并且转换为二进制模式

最后再下载如何避免过帅导致的烦恼.mp4的文件

这要不是最后的文件太吸引我,这么麻烦的步骤,我才不想去倒腾呢

可有了URI后,上面这些步骤,我们只需要在浏览器里直接输入ftp://Naonao:Handsome@naonao.com:8090/Naonao/Source/如何避免过帅导致的烦恼.mp4 这样就可以直接下载网络上的资源了

友情提示一下,上面的URI并不是一个完全正确的URI,因为最后面的中文没有进行转码,关于转码解码的问题,我们稍后再看

什么是URI?

看完上面的内容,URI解决了什么问题,我想各位心里肯定是有数了

不过在了解URI之前,我们需要先简单了解下URL与URN

URL在RFC1738(1994.12)中的定义是Uniform Resource Locator,表示资源的所在位置,期望提供查找资源的方法

而URN在RFC2141(1997.5)中的定义是Uniform Resource Name,期望为资源提供持久的,位置无关的标识方式,并允许简单的将多个命名空间映射到单个URN命名空间

直接说URN这种概念,有些人可能不大清楚是个什么东西,我说个例子,男生肯定清楚,比如磁力链接~嘻嘻,有没有回想起什么不可言论的东西?

我在网上找了个葫芦娃的磁力下载地址,各位围观一下,有没有觉得这东西很眼熟呢?

magnet:?xt=urn:btih:bdab9b6759950fab3c8cbde2669bea6195491034

好咯,不熟悉也没事,这也不是今天的重点,我们知道这玩意大概长城这样就好了。我们现在来看看,URI的定义是什么

URI的全名呢就是Uniform Resource Identifier,主要用于区分资源,它包含了URL与URN的概念,主要是用于取代URL和URN的概念~

换句话说,URI可以是URL/URN,但URL/URN不一定就是URI,也就是说URI是URL/URN的超集

URI与URL的区别

虽然我们现在知道URI是URL的超集,但在网络中,URL与URI这哥俩长的实在太像了,很多时候我们傻傻分不清URL与URI到底谁是谁

我们先来看看定义的区别,URI与URL不同的部分就是IdentifierLocator,URI注重的是唯一标识符,而URL注重的是位置

打个简单的比喻,如果用URI来表述我们自己,那么URI就是我们的身份证号码,URL就是我们身份证上的家庭住址,通过身份证号(URI)肯定能找到我,但是你通过我的住址(URL)那就不一定能找到我了哦

再来说说资源包含了什么

资源这俩字,包含的东西就太广了,既可以是图片、文档,也可以是今天的天气

也可以是不能通过互联网访问的实体,例如人、公司

也可以是某种抽象概念,例如亲属关系或者你是不是渣男

但是要注意一点,URI并不是与资源一一对应的,一个资源是可以拥有很多个URI,但一个URI只会对应一个资源,就像我们手上有很多张银行卡,但每个银行卡对应的开户人,也只有我们自己一个人

Identifier的实际用处就是将当前资源与其它资源区分开来的名称

通过IdentifierSource的含义,我们就可以很明显的感觉到URI的一个目标,它更倾向于资源提供者把自己把所拥有的资源与其它资源区分开

比如不能通过互联网访问的实体,比如人,我们就可以通过URL去定义Mine,Father,Relationship等,通过这种方式,我们就能将我们想表达的资源进行区分

URI的组成

先上张图,我们来看看URI由哪些部分组成

我们根据图片上的内容来进行分析

我们先来看最重要的三点,先拿个例子,看完例子再看下面的说明

https://naonao.com?name=naonao&age=18#page-7

Scheme

Scheme指的就是方案,比如HTTPHTTPSFTP等,都是可以使用的,思想不要被这些常用的协议给局限了,我们还可以自定义协议,只要服务器支持即可

Scheme可以是由字母数字+-.,都是允许的

注意:在Scheme之后,必须使用://把Scheme与后面的部分区分开来

Query

query就是查询参数,是一个可选的参数,如有有的话,那么必须要以?开头

我们最常用的形式就是使用key=value,比如上面的例子name=naonao

但Query并不仅仅是支持这种,它是可以支持pchar,/,?等形式

?的话大家都知道,要使用Query查询参数,那么就必须在前面加上?,而pchar是什么呢?这点我们想了解的话,需要去参考RFC中的详细描述,这不是今天内容的重点

fragment

fragment也是可选的,如果有的话,必须以#开头

比如上面的示例,page-7指向的是一个段落

它所支持的格式跟Query所支持的格式一致

authority

authority包含了用户名与密码(user infomation),还有主机名(host),以及端口号(port)

像用户名密码这东西,我们现在基本已经不使用这种方式了,因为在URI中明文传输账号密码,实在不安全

现在还在用的,基本上也就是经常使用ftp下载资源时我们才使用

所以我们通常只使用host:port,即主机名+端口号的形式

主机名是不可省略的,因为一但省略,我们就找不到对应的服务器

而端口号我们却可以省略,比如HTTP的默认端口号就是80端口,HTTPS的默认端口号就是443端口

path

主机名后面紧跟的就是我们的path

在URI中,path部分必须要以/开头,所以不要把path之前的/误以为是前面authority的结尾

path也分了很多种,分别是path-abemptypath-absolutepath-noschemepath-rootlesspath-empty

  • path-abempty 以/开头的路径或空路径
  • path-absolute 以/开头,但不能以//开头
  • path-noscheme 以非:号开头的路径
  • path-rootless 相对path=noscheme,增加允许以:号开头的路径
  • path-empty 空路径

说这么多种path只是为了尊重文档,但也别看有这么多种类型,其实使用起来是非常简单的,综合上述五种方案,我们可以发现,限制的都是开头的字符

而我们只要不使用中文或者其它一些特殊字符作路径的开头,这样我们的路径都是合法的

所以路径这东西,我们只要根据实际情况进行填写即可

URI的编码

终于到了填坑时间

最开始我们举例说如果世上没有URI时该如何下载资源,我给出的例子URI里面带了中文,其实在URI里只能使用ASCII码

但如果我们的URI里出现了除ASCII码以外的内容,或者是出现了URI中的用于标识的字符比如?``#``/``&等,那么就会引起URI解析错误,那这时候该怎么办呢?

为了避免这种情况出现,URI引入了编码机制

规则非常的简单粗暴,在ASCII码表内的特殊字符,直接就转换成ASCII码

对于ASCII码以外的内容,就转换成十六进制的字节,然后在前面加上一个%,例如空格就被转义成%20?被转译成%3F

像中文这种,十六进制字节值表示不全,需要UTF-8编码才能表述完整的,就是转成十六进制(UTF-8)的格式,例如闹闹就会被转义成%e9%97%b9%e9%97%b9

因为对应的十六进制UTF-8的编码就是E9 97 B9,然后每个字节码前面加上%,就可以得到上述结果了

平时我们在浏览器中的地址栏中输入的URI,就算是输入中文也能正常使用,其实是浏览器在背后帮我们做了转码解码的苦逼活

这其实是一个非常友好的用户体验,不会把一些看不懂的东西直接展示给用户,也是非常值得学习的一个理念

写在最后

URI是网络协议学习中必须要弄明白的一个内容,但其实总的来说并不难,只是概念性的东西稍微多了点,理解了之后,其实就是一点点内容

你可能会问,这东西学了有什么用呢?我只能回答你,学这东西没有直接用处,但是有间接用处

比如做后台开发的,要对接接口,给的URI若不规范,那么接口调用方就无法定位到我们的资源,最后面向Google编程老半天才解决

又或者做前端开发的,接口调用不规范,比如GET调用时query参数写错,那自然也调不通后台给的接口

又比如拿到一个不熟悉的项目,通过浏览器的Network就可以分析到用了哪些资源,依赖了什么页面和接口,但连URI都看不懂,那就只能问同事了,问完后还要被人一顿嫌弃

虽然遇到的这些问题都能面向Google编程或者问同事解决,但是在查资料或者咨询的同时,浪费的是我们的时间以及同事的时间