原创:扣钉日记(微信公众号ID:codelogs),欢送分享,非公众号转载保留此申明。
问题产生
上上周,看到一位老哥找咱们组共事联调接口,不晓得是什么问题,两人坐一起搞了快1个小时,看起来如同有点简单。
忽然,老哥收回一声卧槽,"我传参里的+号,到你这怎么变成了空格!",这个声音很大,我显著的听到了,很快,我就大略Get到了他们的问题点。
我猜想他们遇到的问题大略如下:
- 咱们的接口协议上,都会将申请数据做一次base64编码,而后放到data参数上。
- 而后某些数据做base64编码后有
+
,如{"notes":"代码"}
base64编码为eyJub3RlcyI6IuS7o+eggSJ9Cg==
。 - 而后间接拼到data参数上,即
data=eyJub3RlcyI6IuS7o+eggSJ9Cg==
,组织成http申请收回。
如果写成等价的curl,就是这样:
$ curl http://localhost:8080/send -d 'data=eyJub3RlcyI6IuS7o+eggSJ9Cg=='
写个测试接口调试下看看,如下:
这就是他们遇到的问题,+
会变成空格,这个坑其实蛮容易踩到,我本人刚工作时就踩到过这个坑,也屡次看到或听到他人同踩此坑
问题起因
这个问题和urlencode编码无关,urlencode编码,一般来说,除字母、数字和*
,.
,-
和_
这些字节原样输入外,其它字节都会编码为%XX
(16进制)的模式。
但有一个特例,如下:
String enc = URLEncoder.encode(" ", "UTF-8");System.out.println(enc); // 输入+号String dec = URLDecoder.decode("+", "UTF-8");System.out.println(dec); // 输入空格
特例就是空格会被编码为+
号,反之,+
号会被解码为空格!
注:在新的RFC 2396标准中,空格其实也能够编码成%20,而解码时,+
号与%20都会被解码为空格。
回忆下面的场景,如果将带有+
号的base64字符串,一成不变的封装到data=中,再发送给Tomcat等Web服务器,若Tomcat侧做一次urldecode解码,+
是不是就变成空格了
而Tomcat的确会做urldecode解码这样的操作,当调用方的Content-Type为application/x-www-form-urlencoded
时,这里晓得有这种操作即可,想理解细节可看看我写的这篇文章 由x-www-form-urlencoded引发的接口对接失败
解决问题
解决这种问题,次要有两种办法,如下:
- 调用方对参数做urlencode编码。
按标准来看,当Content-Type为application/x-www-form-urlencoded
时,调用方是必须对参数名与参数值做urlencode的,java实现如下:
String base64Str = Base64.getEncoder().encodeToString(data);String requestStr = "data=" + URLEncoder.encode(base64Str, "UTF-8");
这里做了urlencode后,+
会被编码为%2B
,再由服务端解码,就会变成原样的+
号。
注:如果是应用apache的HttpClient,可思考应用UrlEncodedFormEntity
,它会主动做这个事件。
- 应用urlsafe版本的base64。
一般的base64不能间接作为参数值,因为它可能蕴含+
、/
这两个url不平安的字符,所以base64有个变种叫urlBase64,它将+
、/
替换成了url平安的-
、_
,java实现如下:
String urlBase64Str = Base64.getUrlEncoder().encodeToString(data);String requestStr = "data=" + urlBase64Str;
对于base64、urlencode编码,之前也专门写过一篇文章,感兴趣可进一步浏览 hex,base64,urlencode编码方案比照