在 Openresty 中使用 cjson Lua 模块解析缺少字段的 JSON

Posted

技术标签:

【中文标题】在 Openresty 中使用 cjson Lua 模块解析缺少字段的 JSON【英文标题】:Parse JSON with missing fields using cjson Lua module in Openresty 【发布时间】:2021-07-24 04:08:28 【问题描述】:

我正在尝试解析通过 POST 请求发送到 nginx/Openresty location 的 json 有效负载。为此,我将 Openresty 的 content_by_lua_block 与其 cjson module 结合起来,如下所示:

# other locations above

location /test                                                                                                                
    content_by_lua_block                                                                                                  
        ngx.req.read_body()                                                                                            
        local data_string = ngx.req.get_body_data()                                                                    
                                                                                                                        
        local cjson = require "cjson.safe"                                                                             
        local json = cjson.decode(data_string)                                                                         
                                                                                                                        
        local endpoint_name = json['endpoint']['name']                                                             
        local payload = json['payload']                                                                            
        local source_address = json['source_address']                                                              
        local submit_date = json['submit_date']                                                                    
                                                                                                                            
        ngx.say('Parsed')                                                                                     
                                                                                                                            
                                                                                                                          
                                                                                                               

解析包含所有必填字段的示例数据按预期工作。正确的 JSON 对象可能如下所示:


    "payload": "the payload here",
    "submit_date": "2018-08-17 16:31:51",
    ,
    "endpoint": 
        "name": "name of the endpoint here"
    ,
    "source_address": "source address here",

但是,用户可能会将不同格式的 JSON 对象发布到 location。假设一个简单的 JSON 文档,如


    "username": "JohnDoe",
    "password": "password123"

不包含所需的字段/键。

根据cjson module docs,如果遇到无效数据,使用cjson(没有safe 模式)将引发错误。为了防止出现任何错误,我决定通过导入cjson.safe 来使用它的safe 模式。这应该为无效数据返回 nil 并提供错误消息而不是引发错误:

如果遇到任何无效数据,cjson 模块将在 JSON 转换期间抛出错误。 [...]

cjson.safe 模块的行为与 cjson 模块相同,除非在 JSON 转换期间遇到错误。出错时,cjson_safe.encodecjson_safe.decode 函数将返回 nil,后跟错误消息。

但是,在我的案例中,我没有遇到任何不同的错误处理行为,Openresty 的 error.log 文件中显示了以下回溯:

2021/04/30 20:33:16 [error] 6176#6176: *176 lua entry thread aborted: runtime error: content_by_lua(samplesite:50):16: 尝试索引字段“endpoint”(一个 nil 值)

这又会导致内部服务器错误:

<html>                                                                                                                                 
<head><title>500 Internal Server Error</title></head>                                                                                  
<body>                                                                                                                                 
<center><h1>500 Internal Server Error</h1></center>                                                                                    
<hr><center>openresty</center>                                                                                                         
</body>                                                                                                                                
</html> 

我认为一种解决方法可能是编写一个专门的函数来解析 JSON 数据并使用 pcall() 调用它来捕获任何错误。但是,这会使safe 模式变得毫无用处。我在这里错过了什么?

【问题讨论】:

【参考方案1】:

您的“简单 JSON 文档”是一个有效 JSON 文档。您遇到的错误与cjson 无关,这是一个标准的 Lua 错误:

resty -e 'local t = foo = 1; print(t["foo"]); print(t["foo"]["bar"])'
1
ERROR: (command line -e):1: attempt to index field 'foo' (a number value)
stack traceback:
    ...

cjson.safe 的“安全性”是关于格式错误文档的解析:

cjson 模块报错:

  resty -e 'print(require("cjson").decode("[1, 2, 3"))' 
  ERROR: (command line -e):1: Expected comma or array end but found T_END at character 9
  stack traceback:
      ...

cjson.safe 返回nil 和错误消息:

resty -e 'print(require("cjson.safe").decode("[1, 2, 3"))'
nilExpected comma or array end but found T_END at character 9

【讨论】:

感谢您在此上下文中提供一些详细信息。正如我昨天发现的那样,我的根本问题是试图访问一个嵌套表,其中第一级键是nil。所以基本上我试图访问table[nil]['other_key']

以上是关于在 Openresty 中使用 cjson Lua 模块解析缺少字段的 JSON的主要内容,如果未能解决你的问题,请参考以下文章

Lua+OpenResty应用开发

openresty+lua做接口调用权限限制

lua常用一些功能

【Lua】cjson解析null

openresty开发系列25--openresty中使用json模块

cocos2dx lua 3.10 使用cjson