介绍 URL 编码
URL 编码也被称为百分号编码。
URL 编码的规则:简单来说,如果需要对一个字符进行 URL 编码,首先需要判断该字符是否是 ASCII 字符:
- 如果一个字符是 ASCII 字符,那么对该字符进行 URL 编码,首先需要把该字符的 ASCII 的值表示为两个 16 进制的数字,然后在其前面放置转义字符 %,就得到了该字符的 URL 编码结果。
- 如果一个字符是非 ASCII 字符,那么对该字符进行 URL 编码,首先需要使用指定的字符编码方式(建议使用 UTF-8 字符编码),将 “非 ASCII 字符” 编码为字节序列(字节序列即二进制数据);然后对其字节序列进行 URL 编码。URL 编码 “二进制数据”,首先需要把 “二进制数据” 表示为 8 位组的序列,然后在每个 8 位组的前面放置转义字符 %,就得到了 “二进制数据” 的 URL 编码结果。
技术是为了解决问题而生的,URL 编码的作用是:使用 “安全的字符”(允许出现的字符、无歧义的字符) 替换 “不安全的字符”(不允许出现的字符、有歧义的字符)
- 将 “非 ASCII 字符” 编码为 “ASCII 字符”,便于在 URL 中传输非 ASCII 字符。(URL 中只能出现 ASCII 字符,不能出现非 ASCII 字符)
- 将 “空格” 编码为 “%20”,便于在 URL 中传输空格。(URL 中不能出现空格)
- 将 “没有表示特殊含义的保留字符” 进行 URL 编码。(URL 中多个查询参数之间用 & 符号分隔。如果参数值中包含了 & 字符,那么会对 URL 解析造成影响,因此需要对造成歧义的 & 符号进行编码)
URL 编码的规则
URL 编码需要遵循 RFC 3986 标准。RFC 3986: Uniform Resource Identifier (URI): Generic Syntax (rfc-editor.org)
RFC3986 协议规定 URL 只允许包含两类字符:“保留字符” 和 “未保留字符”。“保留字符” 和 “未保留字符” 都属于是 ASCII 字符。
- 保留字符:“保留字符” 是那些具有特殊含义的字符,比如:斜线字符 / 用于 URL 不同部分的分界。常见的 “保留字符” 有:冒号 :(分隔协议 和 主机)、斜线 /(分隔主机 和 路径)、问号 ?(分隔路径 和 查询参数)、等于号 =(分隔参数 和 参数值)、and 符号 &(分隔多个查询参数)
- 未保留字符:“未保留字符” 没有那些特殊的含义。“未保留字符” 有:大小写字母 a - z、阿拉伯数字 0 - 9、连字号 - 、下划线 _ 、句号 . 、波浪号 ~
对 “保留字符” 进行 URL 编码:如果一个 “保留字符” 在特定上下文中具有特殊含义,并且 URL 中必须使用该 “保留字符” 用于其它目的,那么必须对不表示特殊含义的 “保留字符” 进行 URL 编码。(比如,斜线字符 / 用于 URL 不同部分的分界,但是斜线字符 / 又需要出现在 URL 一个路径成分的内部)
URL 编码一个 “保留字符”,首先需要把该 “保留字符” 的 ASCII 的值表示为两个 16 进制的数字,然后在其前面放置转义字符 %,置入 URL 中的相应位置。(比如,URL 编码斜线字符 /,斜线字符 / 的 ASCII 的值为 47,10 进制的 47 等于 16进制的 2F,因此斜线字符 / 经过 URL 编码后为 %2F)
对 “未保留字符” 进行 URL 编码: “未保留字符” 不需要进行 URL 编码。如果两个 URL 的差别仅在于 “未保留字符” 是用 URL 编码还是用字符自身表示,那么这两个 URL 具有等价的语义。
对 “百分号 %” 进行 URL 编码:由于 “百分号 %” 用于 URL 编码,因此用于 URL 内部的 “百分号 %” 应该被编码。 “百分号 %” 的 URL 编码结果为 "%25"。
对任意数据进行 URL 编码:
- 对 “二进制数据” 进行 URL 编码:URL 编码 “二进制数据”,首先需要把 “二进制数据” 表示为 8 位组的序列(8 位组的序列是将二进制数据按 8 位分组),然后将每个 8 位组表示为两个 16 进制的数字,然后在其前面放置转义字符 %,就得到了 “二进制数据” 的 URL 编码结果。
- 对 “非 ASCII 字符” 进行 URL 编码:URL 编码一个 “非 ASCII 字符”,首先需要使用指定的字符编码方式(建议使用 UTF-8 字符编码),将 “非 ASCII 字符” 编码为字节序列(字节序列即二进制数据);然后对其字节序列进行 URL 编码。
encodeURI 和 encodeURIComponent 方法
encodeURI() 和 encodeURIComponent() 这两个方法是 JavaScript 中进行 URL 编码的方法。
介绍 encodeURI() 方法:
- encodeURI() 方法编码的字符范围:非 ASCII 字符、保留字符的小部分,包括 [] 不包括 !#$&'()*+,/:;=?@
- encodeURI() 方法解码使用 decodeURI() 方法
介绍 encodeURIComponent() 方法:
- encodeURIComponent() 方法编码的字符范围:非 ASCII 字符、保留字符的大部分,包括 #$&+,/:;=?@[] 不包括 !'()*
- 可见 encodeURIComponent 比 encodeURI 编码的范围更广。因此当你需要编码整个 URL 就使用 encodeURI;当你只需要编码 URL 中的参数时,就使用 encodeURIComponent
- encodeURIComponent() 方法解码使用 decodeURIComponent() 方法
application/x-www-form-urlencoded
https://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1
当 HTML 表单中的数据被提交时,表单的域名与值被编码并通过网络把数据发送给服务器。这里的编码方法采用了一个非常早期的通用的 URL 编码方法,并且有很多小的修改,如换行规范化 以及 把空格符的编码 "%20" 替换为 "+"。按这套方法编码的数据的 MIME 类型是 application/x-www-form-urlencoded,当前仍用于 HTML 与 XForms 规范中。
如果发送的是 HTTP GET 请求,application/x-www-form-urlencoded 数据包含在所请求 URL 的查询参数中。如果发送的是 HTTP POST 请求,数据被放置在请求主体中,媒体类型的名字被包含在 Content-Type 请求首部字段。
URL 编码的注意事项
Java 中的 URLEncoder.encode() 方法把 “空格符” 编码为"+",而不是 "%20"。
如果我们把带空格的字符串的编码结果发送给前端。前端调用 decodeURIComponent() 解码时,加号 + 将无法解码为空格。
// encode = say+hello
String encode = URLEncoder.encode("say hello", Charset.defaultCharset().displayName());
解决方案:
- 对 URL 编码结果,调用 String 的 replace(),将 + 号替换为 %20
- 使用其他的 URL 编码工具:可以使用 Spring 提供的 UriUtils 来代替 URLEncoder(推荐使用)