概念解释:
- encode === ‘编码’
- decode === ‘解码’
一句话:
特殊字符(即URL保留字符)和非ascii编码集的字符,尽量都 encode
我们一下几个实际场景:
1. 从服务端获取下发到页面的一个URL,然后需要在App中打开(比如Push消息)
- 首先我们需要明确一点: baidu.com/s?wd=满帮 这个网址是不符合规范的(因为URL中不能包含非 ASCII 之外的编码集,有的话需要对其进行 Encode)
- 但有同学可能会有疑问,为何我在浏览器地址栏里面经常会看到URL中有中文呢(如下图):
- 因为浏览器在显示的时候,浏览器对中文进行了 decode
- 浏览器毕竟是商业化产品,需要考虑用户的实际使用感受,否则地址栏是一串encode过后的字符,用户成本极高
-
那么比较规范的网址是什么呢?
- 如果我们全选、复制下图红框的内容,到编辑器里,会发现其实是:
https://www.baidu.com/s?wd=%E6%BB%A1%E5%B8%AE // 也就是 https://www.baidu.com/s?wd=encodeURIComponent('满帮') 复制代码
即:
也就是对于非 Ascii 编码的字符集需要进行编码,否则 Native 路由如果不兜底的话,Webview 是无法打开对应页面的
- 对于通过 Webview 打开页面而言,也需要对URL Query中的中文进行encode,举个例子:
/** * 列表 -> 详情页 * 需要对中文进行 encode */ function toDetailPage() { const url = `https://taobao.com/detail?id=123`; // 跳转没问题 // 这里「哈哈哈」需要Encode 才能保证在App中正确打开 const url = `https://taobao.com/detail?id=${encodeURIComponent('哈哈哈')}`; JSBridge.schemaJump('taobao://view/webview?url=' + encodeURIComponent(url)); } 复制代码
-
我司 iOS Router 层并没有兜底,要求 URL 生产方需要按照规范对URL中的中文进行Encode,自己尽量不兜底。Android 为了以防万一,做了一次兜底。
-
结论:对于中文作为URL参数的时候,请大家遵循规范,对URL 中的中文 主动进行encode
-
额外场景:有时候前端同学不一定是 URL 的生产方,举个例子:
- Push消息后台有一个输入框,可以让业务同学填写 Push消息对应的页面,业务同学并不懂什么是 Encode,以及什么时候需要Encode。
- 业务同学可能会直接填:
baidu.com/s?wd=哈哈
。这样的URL在下发到App 的时候,就会发现在 iOS端无法打开。 - 写这篇文章的主要目的,就是想让咱们研发同学在开发功能有意识:这个URL是业务同学输入的,会存在各种可能,会存在中文、特殊字符等等情况,应该对输入的URL 参数进行解析然后encode,保证最终下发到端上的URL是符合规范的
- 至于是后端还是前端来做这层encode,那就可以是具体情况,具体协商了,目的是有这个意识并且并且提前规避因为这个可能引发的种种问题。
-
在URI RFC 3986 规范中, 对URL参数何时需要 encode 有说明,我们对其进行进行简单梳理下:
- 非保留字符无需 encode ,保留原样即可
- 保留字符需要根据不同的上下文位置进行转义,即在不同的URI结构部分中,保留字的是否需要转义不太一样,但不论是否有特殊意义,都转义也没错,如果不知道是否需要转义,可以都转义
- 非保留字符:即这些字符在URL中没有特殊含义;
- 保留字符:保留字符在URL中,该字符是有特殊含义的,比如#在URL中是用来做锚点定位的,&是用来分割query参数的
举个例子:
1. 检索「amh」 http://baidu.com/s?wd=amh // 这个URL参数是无需 encode 的 2. 如果我想检索:「&」是什么含义 // 如下的URL是无效的(可以贴在浏览器试一下,会发现打开是百度首页)。 http://baidu.com/s?wd=& // 想要生效,需要对「&」进行encode的,即: http://baidu.com/s?wd=%2d ,即 http://baidu.com/s?wd=encodeURIComponent('&') 3. 可能这里大家还是有点迷惑,为何 & 需要被 encode,再举一个例子: http://baidu.com/s?wd=&iswhat 解释: 因为「&」在 URL 中是保留字符,如果不对 wd 进行 encode,URL的含义就会存在二义性了:你是想搜索: { wd: '&iswhat' } 还是:{ wd: '', iswhat: '' } ?? 也就是说,想搜索 &iswhat,下面网址才是对的: http://baidu.com/s?wd=%26iswhat // 即: http://baidu.com/s?wd=encodeURIComponent('&iswhat') 复制代码
- 如果不是保留字也不是非保留字,那么必须编码
- 需要先将字符转为utf-8字节序列,然后每个字节转为:% + 字节的十六进制表示
- 参考上面的中文字符 encode
结论:需要对上图中 Reserved Character(保留关键字) + 非Ascii 编码的字符集合进行 encode
再来一个例子吧: 站外H5页面需要唤起App中某页面(可能为Native/H5)
-
直接跳转App页面:
taobao://rn.transaction/user-profile?id=123
-
直接跳转App页面,该页面取URL query参数,作为页面中按钮的跳转地址:
taobao://rn.transaction/user-profile?id=123&next-page=encode(baidu.com?id=123)
-
先跳转到App统一中转页面,然后中转页面,然后中转页面再进行跳转到具体页面:
taobao://middle-page?page=encode(rn.transaction/user-profile?id=123&next-page=encode(baidu.com?id=123))
可以看下下面的这个case:
toDetail(id) {
* 1. 从[文章列表]跳转到[文章详情页]的时候,需要带 articleId:1234 过去,且详情页下面有一个按钮,可以跳转到订单详情页(taobao://user/order-detail?id=88888)
*
* 但是下面这种写法,不encode 的话,会造成歧义:
* app-url=taobao://user/order-detail?orderId=88888&articleId=1234 这时候不知道 articleId 是文章详情页的参数还是 订单详情页的参数了
* 解决方案:将 taobao://user/order-detail?orderId=88888 进行 encode,将相关内容作为一个整体,即:
* detail?app-url=taobao%3A%2F%2Fuser%2Forder-detail%3ForderId%3D88888&articleId=1234
*/
toDetail(id) {
const url = `https://taobao.com/detail?articleId=1234&app-url=taobao://user/order-detail?orderId=88888`;
const url = `https://taobao.com/detail?app-url=taobao://user/order-detail?orderId=88888&articleId=1234`;
JSBridge.schemaJump('taobao://view/webview?url=' + encodeURIComponent(url));
}
复制代码