Lua - 空值判断的几种情况
0x00 背景
最近在写一段nginx+redis的代码,主要基于openresty,其中使用到了lua-resty-redis库。我平时写代码都比较小心,针对外部输入的值一般都会进行异常判断,大概的代码如下:
local redis = require "redis" local cjson = require "cjson" --[[省略部分代码]] local ok, err = redis:get("key") if not ok then ngx.log(ngx.ERR, '[ERROR]:', err) return end local data = cjson.decode(ok)
在decode这里出现了错误提示,但是ok并没有为空或者nil,不然代码是走不到这里来。
发现问题后,我们就在前面打印一下ok数据的类型吧,大概的代码如下:
ngx.log(ngx.ERR, 'ok type: ', type(ok)) if not ok then -- TODO end
这个时候我们得到的结果是userdata,这个东西算是一种复杂结构体,一般都是跨语言产生的,比如ffi.C这些。当时我的思路大概也是这样,肯定redis存放的数据是二进制的,但是呀,存放什么数据都是我自己控制的,不可能有什么畸形数据,因此这一点也排除了。最后在自己查看中发现,其实就是这个key不存在。
0x01 分析
既然原因找到了,我们就去看看为什么会这样,主要通过阅读lua-resty-redis的源码:
local function _read_reply(self, sock) local line, err = sock:receive() if not line then if err == "timeout" and not rawget(self, "_subscribed") then sock:close() end return nil, err end local prefix = byte(line) if prefix == 36 then -- char '$' -- print("bulk reply") local size = tonumber(sub(line, 2)) if size < 0 then return null end local data, err = sock:receive(size) if not data then if err == "timeout" then sock:close() end return nil, err end local dummy, err = sock:receive(2) -- ignore CRLF if not dummy then return nil, err end return data elseif prefix == 43 then -- char '+' -- print("status reply") return sub(line, 2) elseif prefix == 42 then -- char '*' local n = tonumber(sub(line, 2)) -- print("multi-bulk reply: ", n) if n < 0 then return null end local vals = new_tab(n, 0) local nvals = 0 for i = 1, n do local res, err = _read_reply(self, sock) if res then nvals = nvals + 1 vals[nvals] = res elseif res == nil then return nil, err else -- be a valid redis error value nvals = nvals + 1 vals[nvals] = {false, err} end end return vals elseif prefix == 58 then -- char ':' -- print("integer reply") return tonumber(sub(line, 2)) elseif prefix == 45 then -- char '-' -- print("error reply: ", n) return false, sub(line, 2) else -- when `line` is an empty string, `prefix` will be equal to nil. return nil, "unknown prefix: \"" .. tostring(prefix) .. "\"" end end
从上面的源码可以看到,在读取redis服务器返回数据的时候,如果某些格式不正确,比如数据长度的字节小于0这样的异常情况,函数就会返回null,注意是null不是nil。
这个null的定义来自ngx.null,这个东西可以追溯到其官方文档lua-nginx-module.
The ngx.null constant is a NULL light userdata usually used to represent nil values in Lua tables etc and is similar to the lua-cjson library’s cjson.null constant.
从上面描述看,ngx.null就是一个代表null的userdata结构,类似一个自定义的类,但是没有什么具体含义,同时文档里面也提到了类似的值还有cjson.null,以后小心被坑。
0x02 扩展
同时文档中还提到了,使用ngx.log对几个空值进行字符串打印的时候
nil会显示成“nil”,
逻辑值会显示成“true”或者“false”,
ngx.null会被显示成“null”。
- 点赞
- 收藏
- 关注作者
评论(0)