Разберите JSON с отсутствующими полями, используя модуль cjson Lua в Openresty

Я пытаюсь проанализировать полезную нагрузку json, отправленную через запрос POST в NGINX/Openresty location. Для этого я объединил 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документации модуля, использование cjson (без его режима safe) вызовет ошибку, если будут обнаружены недопустимые данные. Чтобы предотвратить появление каких-либо ошибок, я решил использовать его режим safe, импортировав cjson.safe. Это должно вернуть nil для недопустимых данных и предоставить сообщение об ошибке вместо того, чтобы вызывать ошибку:

Модуль cjson выдаст ошибку во время преобразования JSON, если обнаружатся какие-либо недопустимые данные. [...]

Модуль cjson.safe ведет себя идентично модулю cjson, за исключением случаев, когда во время преобразования JSON возникают ошибки. При ошибке функции cjson_safe.encode и cjson_safe.decode возвращают nil, за которым следует сообщение об ошибке.

Однако в моем случае я не сталкиваюсь с каким-либо другим поведением при обработке ошибок, и в файле error.log Openresty показана следующая обратная трассировка:

2021/04/30 20:33:16 [ошибка] 6176#6176: *176 поток записи lua прерван: ошибка времени выполнения: content_by_lua(samplesite:50):16: попытка проиндексировать поле «конечная точка» (нулевое значение)

Что, в свою очередь, приводит к внутренней ошибке сервера:

<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 бесполезным. Что мне здесь не хватает?


person albert    schedule 30.04.2021    source источник


Ответы (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
    
person un.def    schedule 01.05.2021
comment
Спасибо за предоставление некоторых деталей в этом контексте. Как я понял вчера, моя основная проблема заключалась в попытке доступа к вложенной таблице, где ключ первого уровня был nil. Так что в основном я пытался получить доступ к table[nil]['other_key']. - person albert; 01.05.2021