一个多层嵌套URL引发的URLEncode血案

URL里中文字符正确过滤姿势

今天有个同事跟我反馈,有个复杂参数的路由跳转需求,目前路由解析满足不了。
这个需求是服务推送一个URL scheme到app,又app的路由寻找到对应的页面。
这个URL结构大致如下:

1
blacktea://gotoWebView?url=https://m.alibaba.com?key1=value1&url2=https://www.blacktea.com.js&key3=value3

第一层嵌了一个参数url,第二层url的value里又嵌了一个参数url2。

服务端对这个url做了encode,推送到app的字符串如下:

1
blacktea://gotoWebView?url=https%3a%2f%2fm.alibaba.com%3fkey1%3dvalue1%26url2%3dhttps%253a%252f%252fwww.blacktea.com.js%26key3%3dvalue3

在路由里为了避免空格和中文字符,会对传入的url分别调用stringByReplacingPercentEscapesUsingEncoding和stringByAddingPercentEscapesUsingEncoding

1
2
urlString = [urlString stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
urlString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

问题来了,因为stringByReplacingPercentEscapesUsingEncoding会对”%3a%2f%3d%26%3f”等被转义的保留字符反转义回”:/=&?”。
而stringByAddingPercentEscapesUsingEncoding只会对中文非法字符做percent转义,不会处理”:/=&?”等保留字符做encode处理。所以最后经过路由后,URL变成如下:

1
blacktea://gotoWebView?url=https://m.alibaba.com?key1=value1&url2=https%253a%252f%252fwww.blacktea.com.js&key3=value3

这就导致第一层的参数url里的链接没有被encode,url2、key3这两个参数都被错认为是第一层的参数了。

路由层做过滤的实际意图是希望,把中文字符转义掉,同时又不影响url各层参数的encode状态。
解决的办法是使用如下两个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static NSString *ASCRouterReplacingPercentEscapes(NSString *string, NSStringEncoding encoding){
NSString *result = ( NSString *)CFBridgingRelease(CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault,
(__bridge CFStringRef)string,
NULL,
CFStringConvertNSStringEncodingToEncoding(encoding)));
return result;
}
static NSString *ASCRouterAddingPercentEscapes(NSString *string, NSStringEncoding encoding) {
return (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
(__bridge CFStringRef)string,
NULL,
NULL,
CFStringConvertNSStringEncodingToEncoding(encoding)));
}

使用以上2个方法,便只会对中文非法字符进行转义,不会影响其他保留字符的转义情况。

多层嵌套url正确的encode姿势

除此之外,对于多层嵌套的url需要正确地encode.

1
blacktea://gotoWebView?url=https://m.alibaba.com?key1=value1&url2=https%253a%252f%252fwww.blacktea.com.js&key3=value3

对于上面例子这个url,需要做2层的urlencode

错误encode方式:

blacktea%3a%2f%2fgotoWebView%3furl%3dhttps%3a%2f%2fm.alibaba.com%3fkey1%3dvalue1%26url2%3dhttps%3a%2f%2fwww.blacktea.com.js%26key3%3dvalue3

正确encode方式:

第一次encode
from:
https://www.blacktea.com.js
to:
https%3a%2f%2fwww.blacktea.com.js

第二次encode

from:
https://m.alibaba.com?key1=value1&url2=https%3a%2f%2fwww.blacktea.com.js&key3=value3

to:
https%3a%2f%2fm.alibaba.com%3fkey1%3dvalue1%26url2%3dhttps%253a%252f%252fwww.blacktea.com.js%26key3%3dvalue3

result:

1
blacktea://gotoWebView?url=https%3a%2f%2fm.alibaba.com%3fkey1%3dvalue1%26url2%3dhttps%253a%252f%252fwww.blacktea.com.js%26key3%3dvalue3

客户端拿到这个被多层encode的url后,再一层一层能地解开,没解开一层都要decode一遍。

UrlEncode编码原理

编码原理:
将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位做一位,前面加上%,编码成%XY格式。

比如:
空格ASCII码是32,对应16进制是20,那么urlencode编码结果是:%20
比如:
中ASCII码是-10544,对应的16进制是FFFFFFFFFFFFD6D0,那么urlencode编码结果是:%D6%D0

URI统一资源标识符里允许的所有字符里,分为保留字符和非保留字符。百分号编码(Percent-encoding)会把保留字符转义为特殊字符序列。

百度百科urlencode

Blacktea wechat
ex. subscribe to my blog by scanning my public wechat account
记录生活于感悟,您的支持将鼓励我继续创作!