复制个带中文的网址,粘贴出来变成一串百分号加数字,很多人以为出错了,其实那是URL编码,也叫百分号编码。它是为了让非ASCII字符和特殊符号能安全塞进网址里。搞懂它的原理,你就能看懂为什么有的链接一长串百分号,也能排查前端传参乱码的问题。
为什么需要编码
URL最早设计时只支持ASCII字符集,而且其中一些字符被保留作分隔用途,比如问号、井号、等号、斜杠、与号。如果URL里直接出现这些字符,解析器会以为它们是结构符号,数据就乱了。还有中文这种非ASCII字符,原始URL规范根本不认。所以要有一套编码机制,把这些字符转成只含ASCII安全字符的形式,这就是百分号编码。
规则是:把不安全或非ASCII的字符,按某种字符编码通常是UTF-8转成字节序列,每个字节写成百分号加两位十六进制。比如空格的字节是十六进制二十,编码后是百分之二十。这样所有字符都能用ASCII安全表示。
哪些字符不用编码
有一类字符叫非保留字符,在URL里任何位置都不用编码,直接出现即可。它们是大小写字母A到Z、数字零到九,以及四个符号连字符、下划线、点号、波浪号,一共六十三个。这些是安全的,编码器不会动它们。
另一类是保留字符,因为URL结构需要它们,比如冒号、斜杠、问号、井号、方括号、at符号、感叹号、美元、与号、单引号、圆括号、星号、加号、逗号、分号、等号。这些字符在URL不同位置含义不同,如果它们是当数据用而不是结构用,就必须编码。比如查询参数值里有个与号,不编码会被当成参数分隔符,数据就截断了。
中文怎么编码
中文是非ASCII字符,编码过程是先按UTF-8转成字节,再每字节百分号编码。比如汉字中的UTF-8编码是三字节十六进制E4 B8 AD,URL编码后是百分之E4百分之B8百分之AD,每个字节一个百分号。所以一个中文在URL里会变成三段百分号,这就是为什么带中文的网址那么长。
注意编码要按UTF-8还是GBK取决于环境。现代网页基本统一UTF-8,但老系统或某些中文环境可能用GBK,同样的字按GBK编码出来的字节不一样,百分号序列也不同。前后端编码不一致就是中文乱码的常见根源。现在统一UTF-8最省心。
空格的两种处理
空格的编码有个历史遗留。标准URL编码里空格是百分之二十。但在表单提交的application/x-www-form-urlencoded格式里,空格编码成加号。这是因为早期规范为了可读性把空格简化成加号。所以你看到加号在表单数据里代表空格,但在URL路径里加号就是个普通字符要编码成百分之二B。这两个场景别混,否则前端解析会错。
现代JS里encodeURI和encodeURIComponent处理也不同。encodeURI不编码URL结构字符如斜杠问号井号,适合编码整个URL。encodeURIComponent会编码这些结构字符,适合编码单个参数值。传参时用encodeURIComponent更安全,能避免参数里的特殊字符破坏URL结构。
常见问题
【URL编码和Base64什么区别?】目的不同。URL编码是为了让任意字符能安全出现在URL里,用百分号加十六进制表示,可逆但体积膨胀三倍。Base64是为了把二进制数据用ASCII表示,用六十四个字符,体积膨胀三分之一。两者都解决ASCII兼容但场景不同,URL编码面向文本字符,Base64面向二进制。
【为什么有的网址中文显示有的显示百分号?】现代浏览器地址栏会把编码后的中文解码显示成中文方便人看,但实际请求时仍发编码后的百分号序列。复制出来变成百分号是原始形态。两者是同一URL的不同显示。
【加号到底代表什么?】看场景。在查询字符串application/x-www-form-urlencoded里加号是空格。在URL路径或用encodeURIComponent编码时加号是字面加号字符。后端解析要按场景区分,不然会把加号误解析成空格。
【URL编码后还能再编码吗?】能,会产生双重编码。比如百分之二十里的百分号再编码变成百分之二十五二十,解码时只解一层得到百分之二十而非空格。双重编码是bug常见原因,排查时看百分号是否被再次编码。