Open jinhailang opened 7 years ago
+
的编码问题今天又遇到一个坑,使用 ngx.unescape_uri
解码函数,会将 +
转换成 `(空格)[openresty 讨论贴](https://github.com/openresty/lua-nginx-module/issues/941),[W3C标准规定](https://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1) 规定的应该是参数部分编码的时候,将
转成
+,而路径部分应该还是转码成
%2B,而
ngx.escape_uri又会将
+转成
%2B,空格转成
%20,所以这里应该是个 bug,而且
ngx.var.uri解码的时候是不会转换
+`,golang 也是正常的。
解决方案,是自己封装路径编码函数 unescape_path
:
function _M.unescape_path(path)
local s = gsub(path, "([^+]+)", function (s)
return unescape_uri(s)
end)
return s
end
url 特殊字符编码
RFC 3986 指定了保留字符,分为两类,如下:
可能只会选择对其中的部分保留字符编码,而不同的编程语言和函数选择编码的字符不一样,这就导致不同系统之间的解析出来的 http url 不一致。
以 Go 为例,运行下面两段代码。
代码 1:
输出:
代码 2:
输出:
运行以上两段代码,会发现输出的 url string 有微小的差异,上面的将
'
转码成了%27
,而下面没有转码。按理来说,调用同样的函数,编码后的 url 应该一样才对,问题出在哪儿呢?
查看函数源码发现,函数
url.String
输出的是编码后的 url 字符串。但是,如果输入的原始 url 已经是编码的,那么就不会做处理直接使用原始 url。判断函数validEncodedPath
代码片段:那么,上面两段代码输出结果不一致的原因基本找到了,那就是因为
validEncodedPath
判断认为代码 2 输入的 url 是已经编码过(已被 ngx_lua 编码)的,就将原始字符串输出了。而代码 1 输入的 url 被 Go 函数EscapedPath
编码之后再输出。再进一步查看 Go 的编码函数
escape(s string, mode encoding)
,判断是否需要编码函数shouldEscape(c byte, mode encoding)
判断,代码片段如下:可以看到 Go 会对 url path 中
'
,?
等特殊字符编码,而不会对;
,=
等字符编码。当然更多不同语言的编码差异, 需要具体分析了。对于使用多种编程语言的复杂系统,需要特别注意这点,最好是最后处理 url 的程序自己保持编码一致,即将输入的 url,处理成符合自己的编码规范。因为,一般解码函数(如 Go)没有这种差异,所以可以先对输入的 url 解码,再编码,这样能保证编码的 url 字符串,符合本程序语言规范的,从而保证本程序最终的 url 编码字符串一致总结
所以,最好是不要在 url 中使用这些保留字符。不同的语言会有微小的差异,很容易导致 bug,查找和处理都很麻烦。