# URI
30 秒速记
URI是资源的唯一标识,日常说的网址URL是它最常见的一种- 拆开看:
scheme://host:port/path?query#fragment,协议 / 去哪台机器 / 取哪个资源 / 附加条件 / 页内锚点 #fragment只给浏览器用,不会发给服务器;后端拿不到hash- 中文和
&=/?#这类有结构含义的字符要编码,参数值用encodeURIComponent,别对整条URL用它 - 相对地址按当前页面地址解析,
new URL('../a', base)能直接算出结果
URI 就是资源在网上的唯一地址,平时说的 URL 基本可以当一回事。 一条 https://api.example.com:8443/users/42?expand=profile#contact 拆开看:https 决定用什么协议,api.example.com:8443 决定连哪台机器哪个端口,/users/42 是要哪个资源,?expand=profile 是附加要求,#contact 只是页面内的锚点,浏览器根本不会发给服务器。前端最容易踩的是编码:参数里有中文或者 &,直接拼字符串就会被截断,值要用 encodeURIComponent 包一下,或者干脆交给 URLSearchParams。
const kw = '前端&HTTP'
'/search?keyword=' + kw // 服务端收到 keyword=前端 和一个多余参数
'/search?keyword=' + encodeURIComponent(kw) // keyword=%E5%89%8D%E7%AB%AF%26HTTP
new URLSearchParams({ keyword: kw }).toString() // 同样安全

URI是用来唯一标记服务器上资源的一个字符串,通常也称为 URL;URI通常由scheme、host:port、path和query四个部分组成,有的可以省略;scheme叫“方案名”或者“协议名”,表示资源应该使用哪种协议来访问;- “
host:port”表示资源所在的主机名和端口号; path标记资源所在的位置;query表示对资源附加的额外要求;- 在
URI里对“@&/”等特殊字符和汉字必须要做编码,否则服务器收到HTTP报文后会无法正确处理

原理拆解: 一个绝对地址可写成 https://api.example.com:8443/users/42?expand=profile#contact。其中 https 选择通信方案,主机名交给 DNS 解析,8443 指定目标端口,/users/42 参与服务器路由,expand=profile 作为查询参数发送给服务器。#contact 是片段标识,由客户端定位页面内部位置,普通 HTTP 请求不会把它发送到服务端,因此它不属于服务端资源选择条件。
具体例子: 相对引用会以当前文档地址为基准解析。例如当前地址为 https://example.com/docs/http/index.html 时,../uri.html?lang=zh 会得到 https://example.com/docs/uri.html?lang=zh。路径中的 / 表示层级,查询中的 & 和 = 承担参数分隔语义;若数据本身包含这些字符,应按组件编码,而不是对整个地址不加区分地编码。JavaScript 中可用 encodeURIComponent("前端&HTTP") 得到可安全放入单个参数值的字符串,再由 decodeURIComponent 还原。
边界与排查: URI 是标识符的总称,URL 强调可定位性,日常 Web 地址通常兼具两者性质。空格不能原样出现在规范地址中;中文一般以 UTF-8 字节进行百分号编码。+ 在某些表单查询解析器中会被解释为空格,但在通用 URI 语法里没有统一的“空格”含义。遇到签名不一致、路由 404 或参数被截断,应比较客户端最终请求地址与服务端原始请求目标,重点检查重复编码、大小写敏感路径、尾部斜杠以及代理是否执行了规范化。
面试官追问
追问 1有人想用 #contact 当后端的筛选条件,行不行?
不行,# 后面的内容浏览器不会放进请求里,后端永远拿不到。要让服务端参与的条件放 path 或 query。这也是早年 hash 路由的 SPA 对 SEO 不友好的原因之一。
追问 2encodeURI 和 encodeURIComponent 有什么区别,什么时候用哪个?
encodeURI 会保留 / ? & = # 这些结构字符,适合编码一整条地址;encodeURIComponent 把它们也编掉,适合编码单个参数值。拿 encodeURIComponent 编整条 URL,https:// 会变成 https%3A%2F%2F,地址直接废了。
追问 3当前页是 /docs/http/index.html,../uri.html 最终请求的是哪?
/docs/uri.html。.. 是从当前文件所在目录 /docs/http/ 退一层,不是从站点根开始算。不确定就在控制台跑 new URL('../uri.html', 'https://example.com/docs/http/index.html').href。
追问 4签名接口只在中文关键词时失败,日志里出现了 %25E5,什么情况?
重复编码了。%25 就是 % 本身被编码,说明某一层对已经编码过的字符串又编了一次,比如前端编了一次、请求库又编一次。把客户端发出的原始地址、代理转发的地址、服务端解析后的值逐段比对,找出多编的那层。
追问 5业务 ID 里可能有 /,放 path 里还是 query 里?
能放 query 就放 query,?id=a%2Fb 很稳。放 path 里就算编成 %2F,有些网关和服务器会先解码再路由,或者直接拒掉含 %2F 的路径,结果就是莫名其妙的 404。
