http 链接带有中文的编码问题
# 一、背景
dsp 返回的曝光监测链接中带有中文字符,不对曝光监测链接进行任何编码直接发送 http 请求时,会出现请求失败或是 http 状态码 400 的错误。这个现象的原因可能和客户端有关,url 中包含空格等;也可能与服务端使用的 http 库有关,没有正确解码中文字符。
# 二、解决方法
- 需要对包含中文或是空格的键或值进行 URL encode,并且保证字段原有的顺序不被改变,避免影响服务端接收。
# 1,错误示例
- 先对 url 参数部分解码,再找到是中文的字段进行编码,最后整体编码;
- 并且在
Encode()
方法中,会将所有字段按照键进行排序后再拼接,这种方式可能会打乱原先 url 链接的字段顺序。
// encodeURI 对 URI 进行编码
func encodeURI(uri string) (string, error) {
parsedURL, err := url.Parse(uri)
if err != nil {
return "", err
}
// 编码查询参数部分
queryParams := parsedURL.Query()
for key, values := range queryParams {
for i, value := range values {
if IsChinese(value) {
queryParams[key][i] = url.QueryEscape(value)
}
}
}
// 重复编码的问题
parsedURL.RawQuery = queryParams.Encode()
return parsedURL.String(), nil
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 2,正确示例
- 参考 url 包中的
parseQuery
函数对 url 进行解码,并对包含中文和空格的字段进行编码,并按照解码的顺序存储在切片中; - 参考 url 包中的
Encode
方法对 url 进行编码,只按照原先顺序拼接,而不需要排序。
package main
import (
"fmt"
"net/url"
"strings"
"unicode"
)
func main() {
uri := "https://mkt-advertisement.tuhu.cn/ext/advertisement/bytedancev2/expose?promotion_id=7412981354121789494&project_id=7405943679905677375&promotion_name=20240910-穿山甲-关键行为2-快闪-竖视频-240905cs快闪召回保养空调想要给爱29&project_name=20240822-老客-安卓-保养-RTA定向-VE0610$-穿山甲-保养空调活动&mid1=&mid2=7412607455437570100&mid3=7411430196828913727&mid4=&idfa=&imei=&mac=&os=0&ts=1726305605000&callback_url=http%3A%2F%2Fad.toutiao.com%2Ftrack%2Factivate%2F%3Fcallback%3DCISC6qGzwJsDEKOwgeSzwJsDGKzfkbquAiCNh-zVoQEoADAMOO6QrcgDQiUzZjA0OTNkOGMwNDY2YjRjNjQ4MzQ1YjhiYzk3MjZjZHU1OTAwSIDSk60DUACQAQI%26os%3D0%26muid%3D&csite=900000000&ctype=15&ua=Mozilla%2F5.0+%28Linux%3B+Android+12%3B+NOH-AN00+Build%2FHUAWEINOH-AN00%3B+wv%29+AppleWebKit%2F537.36+%28KHTML%2C+like+Gecko%29+Version%2F4.0+Chrome%2F99.0.4844.88+Mobile+Safari%2F537.36&mac1=d41d8cd98f00b204e9800998ecf8427e&oaid=30400077-5ddd-4100-8772-51adf00bb141&androidid=&advertiser_id=1779248892891149&ip=240e:46d:b900:47e2:e8fe:3340:2655:4b1b&convert_id=0&callback_param=CISC6qGzwJsDEKOwgeSzwJsDGKzfkbquAiCNh-zVoQEoADAMOO6QrcgDQiUzZjA0OTNkOGMwNDY2YjRjNjQ4MzQ1YjhiYzk3MjZjZHU1OTAwSIDSk60DUACQAQI&request_id=3f0493d8c0466b4c648345b8bc9726cdu5900&oaid_md5=6539763b1f7de2629e1533c42e55d8e9&geo=0.0000000x-0.0000000x100.00&sign=&unionSite=2056741088&outerId=&vid=&rta_real=0&model=NOH-AN00&rta_trace_id=a105b6ab8582aae6ae2536e02c447b9du6935&caid=&rta_vid=&test"
fmt.Println(EncodeUrlChineseParam(uri))
}
func EncodeUrlChineseParam(uri string) (string, error) {
pr, err := url.Parse(uri)
if err != nil {
return "", err
}
query := pr.RawQuery
ks := make([]string, 0)
vs := make([]string, 0)
as := make([]string, 0)
for query != "" {
var kv string
kv, query, _ = strings.Cut(query, "&")
if strings.Contains(kv, ";") {
return "", fmt.Errorf("invalid semicolon separator in query")
}
if kv == "" {
continue
}
key, value, ok := strings.Cut(kv, "=")
if IsChinese(key) || strings.Contains(key, " "){
key = url.QueryEscape(key)
}
if IsChinese(value) || strings.Contains(value, " "){
value = url.QueryEscape(value)
}
ks = append(ks, key)
vs = append(vs, value)
a := "="
if !ok {
a = ""
}
as = append(as, a)
}
var buf strings.Builder
for i, k := range ks {
if buf.Len() > 0 {
buf.WriteByte('&')
}
buf.WriteString(k)
buf.WriteString(as[i])
buf.WriteString(vs[i])
}
pr.RawQuery = buf.String()
return pr.String(), nil
}
func IsChinese(str string) bool {
for _, v := range str {
if unicode.Is(unicode.Han, v) {
return true
}
}
return false
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# 三、总结
- url 链接中带有空格时,直接发送 http 请求会报错;如果带有中文字符,且服务端 http 库没有正确处理中文编码时,也会报错。
- 直接使用 go 的 url 库中,对 url 的键值对进行编码时,会将键值对进行排序后再拼接在一起,改变了原有键值对的顺序,对原有的 url 链接改动较大,可能会影响服务端处理,同时也会将一些明文传输的非中文特殊字符进行编码,不符合预期;
- 本文在 url 包源码的基础上提供了一种方法,仅对包含空格或中文的键或值进行编码,以尽可能小的改动原有 url 链接;
编辑 (opens new window)
上次更新: 2024/09/23, 17:53:19