最近做需求时候, 遇到一个后端联调问题, 需求是 后端A 把一个 json 转成 string 后放到 scheme 的payload
参数中, json 如下, text1
是一个字符串,
1 | { |
转换为字符串payload
后变成
1 | {\"text1\":\"今天是个好日子\",\"trackID1\":123456} |
scheme 大致如下:
1 | scheme://test/ui/func?p={"hintPayload":"{\"text1\":\"今天是个好日子\",\"trackID1\":123456}","param2":"9"} |
然后对 scheme 转义, 下发给客户端, 客户端在解析 scheme 后, 获取各个参数, 然后把payload
字符串给另一个业务后端B.
自测阶段没什么问题, 但是测试发现text1
中有换行符以及"
时候就不行了, 客户端问题就出在解析 scheme 时候, 会对参数做一次 string 转 json 处理, 这次处理失败了.
报错如下
1 | parse json error(Error Domain=NSCocoaErrorDomain Code=3840 "Unescaped control |
意思是text1
的第 42 个字符有问题, (注意这里是从 0 开始数的, 但是把有问题的字符串贴到编辑器, 编辑器大多是从 1 开始数的)
客户端 scheme 解析出错
找到有问题的字符是 %0A
, 换行符, 嗯, easy, 让 后端A 把 %0A
换成 \\n
下发就好, 经过 scheme 反转义后会变成 \n
很快遇到第二个问题, "
也有问题, 行, 再让 后端A 换成\\"
下发, 经过 scheme 反转义后变成\"
, 客户端终于能解析 scheme 了.
注: iOS 解析
%0A
以及"
出错, Android 解析"
出错
后台解析也出错
然后就遇到第三个问题, 当把从 scheme 里面解析出来的payload
给另一个业务 后端B 时候, 转json 就失败了,
第一个问题是 字符串里直接是 \n
后端解析不了,
第二个问题是 \"
后端也解析不了
这里尝试客户端做下兼容, \n
换成 \\n
, easy,,,
但是 \"
处理就麻烦了, payload
字符串如下,
1 | { \"text1\": \"今天是个好日子\"有引号\", \"trackID1\": 123456 } |
text1
中有\"
, 参数 text1
/trackID1
后面也有\"
, 直接对字符串处理肯定是不行的
那还得先把 payload
转成 json, 再对其中的值做 \"
的转换, 最后再把 json 转成字符串给到后端B.
疯了吧
最后回想下上面,
- 后端A 做了两个字符串的处理,
- 客户端(iOS / Anrdoid) 两端都也要处理,
- 本不该客户端理解的
payload
字段客户端也得做一次 json 化, 然后处理其中的值, - 还有其他字符有问题吗???
怎么看这个思路都有大问题
追溯源头
回到最开始的问题, 客户端居然在 scheme 中的参数解析出了 %0A
, 百分号字符是 URL 转义的, 客户端在对 scheme 做了反转义之后, 根本不应该还有 %0A
呀? 后端A 下发的 scheme 有问题, 问后端 A scheme 是怎么拼出来的, 答曰: 前端提供的, 直接替换了某些字段, 就转义然后给了客户端.
前端提供的 scheme 如下:
1 | scheme://test/ui/func?p={"hintPayload":"{\"text1\":\"text1Value\",\"trackID1\":trackID1Value}","param2":"9"} |
后端A 使用上面的 scheme 直接把 text1Value
以及trackID1Value
换成了需要的值, 然后转义下就给了客户端. 所以 text1Value
中的特殊字符根本没有被处理过, 直接一起被转义了, 导致了前面一串的问题.
解决
解决问题总是很无聊的, 后台对 scheme 的处理改为
- 使用需要的值, 构造了 json
- json 转为字符串
- 字符串放在了 scheme 后面的参数中
- 转义给了客户端
好像一开始就应该这么做吧? 这反应一个普遍问题, scheme 是前端和客户端的交互的协议, 后端一般不愿意去理解, 最后在实践中也的确没有去理解, 直接通过字符串替换的方式完成了需求, 最后导致了联调问题.