URL 跳转何时Encode小结

概念解释:

  1. encode === ‘编码’
  2. decode === ‘解码’

一句话:
特殊字符(即URL保留字符)和非ascii编码集的字符,尽量都 encode

我们一下几个实际场景:

1. 从服务端获取下发到页面的一个URL,然后需要在App中打开(比如Push消息)

image.png

URL: https://baidu.com/s?wd=满帮

  1. 首先我们需要明确一点: baidu.com/s?wd=满帮 这个网址是不符合规范的(因为URL中不能包含非 ASCII 之外的编码集,有的话需要对其进行 Encode)
  2. 但有同学可能会有疑问,为何我在浏览器地址栏里面经常会看到URL中有中文呢(如下图):

image.png

  1. 因为浏览器在显示的时候,浏览器对中文进行了 decode
  2. 浏览器毕竟是商业化产品,需要考虑用户的实际使用感受,否则地址栏是一串encode过后的字符,用户成本极高
  1. 那么比较规范的网址是什么呢?

    • 如果我们全选、复制下图红框的内容,到编辑器里,会发现其实是:
    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,那就可以是具体情况,具体协商了,目的是有这个意识并且并且提前规避因为这个可能引发的种种问题。
  2. URI RFC 3986 规范中, 对URL参数何时需要 encode 有说明,我们对其进行进行简单梳理下:

    1. 非保留字符无需 encode ,保留原样即可
    2. 保留字符需要根据不同的上下文位置进行转义,即在不同的URI结构部分中,保留字的是否需要转义不太一样,但不论是否有特殊意义,都转义也没错,如果不知道是否需要转义,可以都转义
    • 非保留字符:即这些字符在URL中没有特殊含义;
    • 保留字符:保留字符在URL中,该字符是有特殊含义的,比如#在URL中是用来做锚点定位的,&是用来分割query参数的

    https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/90f14c5f975648929039d7fb468ff41c~tplv-k3u1fbpfcp-zoom-1.image

    举个例子:

    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')
    
    复制代码
    1. 如果不是保留字也不是非保留字,那么必须编码
    • 需要先将字符转为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));
}
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享