前端编码”三剑客“(二)URI

350 阅读2分钟

《前端编码系列》

前端编码“三剑客”(零)前置基础

前端编码"三剑客"(一)Base64

前端编码”三剑客“(二)URI


一、什么是URI

组件的概念

URI components,指URI的各个组成部分,包括:scheme、authority、path、query、fragment

image-20241006224331192

https://user:password@example.com:8080/path/to/resource?query=string#fragment



保留字符

用途:提供一组可与URI中的其他数据区分开的分隔字符

范围:

gen-delims:/?#[]@
sub-delims!$&'()*+,;=
  • gen-delims,用于区分 URI 组件(scheme、authority、path、query、fragment)的分隔符
  • sub-delims,用于区分 URI组件的子组件的分隔符



非保留字符

范围:保留字符外的其它字符,包括但不限于a-zA-Z0-9-._~ (波浪号)、所有非 ASCII 字符(对于非ASCII字符(如中文字符),虽然它们属于非保留字符,但在URI中直接使用可能会导致兼容性问题,所以在百分比编码中,把这些字符也进行了编码)



百分号编码

Percent-Encoding

用途:用于在URI的组件中表示非 ASCII 字符或保留字符,目的是确保URI中的字符不会与URI的语法结构冲突,也不会被错误地解释;同时使得URI可以包含广泛的字符集

实现方式:通过将一个8位字节(octet,指代具有8个bit的实体)转换为一个百分号%后跟两位的十六进制数来实现。

pct-encoded = % hexdig hexdig

字符十六进制百分比编码
&26%26
=3D%3D
?3F%3F

注意:存在2种百分号编码的字符 —— 空格

根据上下文,空白符 ' ' 将会转换为 '+' (如使用百分号编码的 application/x-www-form-urlencoded 消息),或者将会转换为 '%20'(如 URL 中)。



URI和URL

  • URI:Uniform Resource Identifier,统一资源标识符,是一种用于标识某一互联网资源的字符串。主要用来标识,但不一定能定位。
  • URL:Uniform Resource Locator,统一资源定位符,是 URI 的一个子集,专门用于标识和定位互联网上的资源。URL 提供了资源的位置信息,主要用来定位



二、URI编码

什么是URI编码

URI(Uniform Resource Identifier,统一资源标识符)编码是指:将URI中的特殊字符转换成特定格式的过程



为什么要编码

  1. 兼容URI组件中的非 ASCII 字符。URI 只允许包含 ASCII 字符集中的字符,非 ASCII 字符都需要通过「Percent-Encoding 百分比编码」方法进行编码。这是 URI规范定义 RFC3986 的规定,推测这么规定的目的是为了让URI在不同地区之间通用。
  2. 兼容URI组件中的保留字符。如果要在URI的组件内部包含这些字符,需要进行编码,否则可能会导致 URI 解析错误,如将组件中的特殊字符识别为分割符,导致内容被提前分割、解析错误。
  3. 兼容URI组件中的空格和其它控制字符。空格和一些特殊字符(eg:换行符)在URI中是不被允许的,如果要传递这些,就需要进行编码



escape(废弃)

生成新的由十六进制转义序列替换的字符串

console.log(escape("中文"));  //输出:%u4E2D%u6587。ps:“中”的unicode编码值为U+4E2D,“文”的unicode编码值为U+6587



encodeURI

需要创建合法安全完成的URL时,使用该方法。

通过将特定字符替换成代表字符的UTF-8编码的一个、两个、三个或四个转义序列来编码,转义序列的个数取决于UTF-8编码字节数

【编码方式】:百分号编码

UTF-8encodeURI(string)
``(空格)\x20%20
é\xC3\xA9%C3%A9
\xE4\xB8\xAD%E4%B8%AD
𠜎\xF0\xA0\x83\xA7%F0%A0%83%A7

【不编码范围】 :保留的并且在 URI 中有特殊意思的字符

类型包含
保留字符; , / ? : @ & = + $
非转义的字符字母 数字 - _ . ! ~ * ' ( )
数字符号#

【特点】:主要目的是兼容非ASCII字符和一些特殊符号,能够保留URI原始的分隔符不被编码转义

【主要的适用场景】:创建合法、安全的完整URL,URL中并不能直接兼容非 ASCII 字符、以及空格、引号、尖括号等,使用 encodeURI() 可以确保这些字符被正确编码,从而创建出合法的URL。一些场景下需要将用户输入拼接到URL中,使用encodeURI()可以规避危险字符,以及处理前面提到的非法字符的问题。

该方法是幂等调用的吗?答:不是(见下面🌰),因为作为百分号编码起始前缀的 % 会被编码为 %25

console.log(encodeURI("中文")); //输出:%E4%B8%AD%E6%96%87
console.log(encodeURI(encodeURI("中文")));  //输出:%25E4%25B8%25AD%25E6%2596%2587
​
console.log(decodeURI("%E4%B8%AD%E6%96%87")); //输出:中文
console.log(decodeURI("%25E4%25B8%25AD%25E6%2596%2587")); //输出:%E4%B8%AD%E6%96%87



encodeURIComponent

该方法常用于编码URI组件的值,如query子组件(query参数)的值。

image-20250105181153835

通过将特定字符替换成代表字符的UTF-8编码的一个、两个、三个或四个转义序列来编码。与 encodeURI 的区别仅在于编码范围更大

【编码方式】:百分号编码

【不编码的范围】

类型包含
非转义的字符字母 数字 - _ . ! ~ * ' ( )
console.log(encodeURIComponent("aB1-._~!*()'"));  //输出:aB1-._~!*()'

【主要的适用场景】:创建无歧义的URL组件,在URL组件(如query参数)中包含特殊字符(如&)时,需要对特殊字符进行编码,否则会导致URL解析偏离预期。



三、URI解码

decodeURI

顾名思义,解码由encodeURI 创建字符串



decodeURIComponent

顾名思义,用于解码由 encodeURIComponent 方法创建的字符串



参考文献

RFC 3986

escape() | MDN

encodeURI() | MDN

URI Handling Functions | Draft ECMA-262