前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >关于URL Encoding的那些事

关于URL Encoding的那些事

作者头像
Bruce Li
发布2019-12-17 18:13:12
1.1K0
发布2019-12-17 18:13:12
举报

之前遇到一个关于URL encoding的一个问题,很tricky,这里把这个问题的root cause以及对这个问题的一些思考记录下来,分享给大家。

首先,抽象这个问题的原型如下:

有一个电商平台,我们需要调用其暴露的一个API来创建电商信息,API要求我们把电商名作为URL参数传过去,然后创建相应的一条电商记录。这个API示例如下:

代码语言:javascript
复制
POST http://localhost:8080/stores/{storeName}

返回结果:

代码语言:javascript
复制
{
  "storeName": "xxx",
  "otherInfo": ""
}

在测试这个API的过程中发现,有一个电商名(abc{d)包含字符“{”,出现了一个问题:用postman发送请求过去能够成功;但是通过java代码发送请求则报错,提示说URL syntax出错,URL不允许包含“{”字符。

代码语言:javascript
复制
POST http://localhost:8080/stores/abc{d

后来发现,postman能成功的原因是由于postman自动帮忙做了个URL encoding,通过创建出来的那条记录可以验证,创建出来的记录如下。

代码语言:javascript
复制
{
  "storeName": "abc%7Bd",
  "otherInfo": ""
}

而java代码没有做encoding,所以就报错了。因为根据URL的RFC规范,“{”字符确实不允许,必须做encoding。See https://tools.ietf.org/html/rfc3986#section-2。

那么,怎么解决这个问题呢,如何允许传入包含“{”字符的电商名呢?当时想到的解决方案是,在代码中显式的把电商名都做一个URL encoding,然后再作为URL参数传过去。很好,用这个方案把这个问题解决了。请求URL参数电商名是encoded过的(abc%7Bd),创建出来也是encoded过的(abc%7Bd)。

没过多久,又出现了另外一个问题。在测试过程中,有一个电商名(abc:d)包含字符“:”,按照之前的逻辑,我们把“:”做一个encoding(abc%3Ad)发送过去,期望创建的记录应该是encoded的名字,但是结果却不是,结果是一个decoded的名字(abc:d),即包含“:”本身。

结果因为这个,程序出错了。同时测试还发现,通过postman发送请求过去没有问题,发送带字符“:”的名字(abc:d)过去,创建出来的就是带“:”(abc:d);发送字符“:”的encoded串(abc%3Ad)过去,得到的就是encoded的串(abc%3Ad)。但是,通过代码发送字符“:”(abc:d)或者字符“:”(abc%3Ad)的encoded的串,创建出来的都是“:”(abc:d)。到这里就有点思路了,发现字符“:”好像在哪里被自动decode了。

经过调试发现,问题出在一个底层依赖的library的版本上,java发送http请求的方法最终依赖于一个library叫做org.apache.httpcomponents:httpclient,高版本(4.5.8)的library会自动decode一些字符(包括“:”),低版本(4.5.5)的则不会。

那么到这里,问题就明白了,问题的root cause即是虽然我们显式把字符“:”做了一个encoding,但是在请求被真正发出去之前被Httpclient自动decode了,所以创建出来的记录都是“:”。

对这个问题的一些思考:

第一,为啥字符“:”的encoded串会被自动decode,而字符“{”却没有?

原因是,“:”是URL规范允许的字符,尽管其是保留字符。而“{”是不允许字符,必须做URL encoding,中文汉字也是一样,必须做URL encoding。

第二,如果传入的是一个中文字符的encoded串,创建出来的就是encoded的串,很显然这个不易读,所以我们需要把这个encoded的串做一个decode。那么设想一下,服务端每次都需要显示做decode吗?记得之前在Spring mvc项目中没有显示decode啊?

原因是Spring mvc自动把URL参数都做了一个decoding,所以我们不用显示decode。而这个问题中的API在实现端没有利用自动decode功能,即拿的是原生的参数值,所以一些时候会存在不易读。自动decoding会有什么问题吗?在大多数情况下应该是没问题,即输入“{”的encoded串,后端得到的是就是“{”。那么如果本身参数值就是“{”的encoded串呢,也没问题,发请求方自己需要对这个串做一次encoding作为输入。

第三,发现自动decoding在不同技术栈平台(Spring boot / mvc, .net core / mvc, .net framework / mvc, Nodejs)实现不一样 ,有时候也会出现不一致的情况。比如说,当请求的URL参数包含%3F(字符“?”的encoded串),在Spring boot和 .net core都能够正常拿到字符“?”;在.net framework里却会报错。而当请求的URL参数包含%2F(字符“/”的encoded串),在Spring boot, .net core和 .net framework里都不工作;在Nodejs里,用相对比较原生的方式,就可以工作并且获取到这个URL参数。如下:

P1:Spring boot中字符“?”是work的

P2:Spring boot中字符“/”不work,报404

P3:Nodejs中可以拿到包含字符“/”的参数

所以有时候利用平台的自动decoding可能会出现一些问题,这时候你可能需要考虑利用平台相对比较原生的方式操作httprequest对象,比如上面nodejs的方式。

最后,其实关于编码,之前也写过一篇关于utf8编码的文章(关于编码的那些事),这里讨论的是URL encoding。编码,个人理解其本质就是把一种表现形式的内容通过某种方式转换成另外一种形式,以达到某些目的,比如URL encoding把字符“{”转成“%7B”,这样才符合http规范,URL才能被正确解析及转发到服务器;比如utf8编码把字符“汉”转成“e6b189”,这样才能被计算机存储,因为计算机只能识别一个字节一个字节。除了utf8编码、URL encoding,我们常用到的还有另外一种编码方式:base64编码,这个编码主要用于混淆易读的一些信息,比如jwt token。

注意,我们在很多地方还用到哈希算法,比如SHA1等,编码和哈希最大的区别就是:编码是可逆的,哈希不可逆。

References

  • URL规范:https://tools.ietf.org/html/rfc3986#section-2
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-12-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 天马行空布鲁斯 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档