在基於 OpenResty (Nginx + ngx_lua) 的架構中,高效解析 HTTP 請求參數是核心需求。本文詳解從基礎 GET/POST 參數獲取到處理 multipart/form-data 文件上傳的進階 Lua 代碼實現,並分析關鍵 API 與邊界情況。
ngx_lua 提供原生 API 直接獲取解析後的參數表:
local request_method = ngx.var.request_method
local args
if request_method == "GET" then
args = ngx.req.get_uri_args()
elseif request_method == "POST" then
ngx.req.read_body() -- 必須先讀取 body
args = ngx.req.get_post_args()
end
local param1 = args["param1"]
local param2 = args["param2"]
關鍵點:POST 請求必須先調用 ngx.req.read_body() 將請求體讀入內存或臨時文件,否則 get_post_args() 返回空表。
默認 get_post_args() 僅支援 application/x-www-form-urlencoded。若需處理文件上傳 (multipart/form-data),需手動解析請求體。以下為完整封裝函數:
local function parse_multipart_body()
local headers = ngx.req.get_headers()
local content_type = headers["content-type"]
if not content_type or not content_type:find("multipart/form-data", 1, true) then
return nil, "not multipart"
end
ngx.req.read_body()
local body_data = ngx.req.get_body_data()
-- 若 body 過大被寫入臨時文件,需從文件讀取
if not body_data then
local file_name = ngx.req.get_body_file()
if not file_name then return nil, "no body file" end
local fh, err = io.open(file_name, "rb")
if not fh then return nil, "open file failed: " .. err end
body_data = fh:read("*a")
fh:close()
end
-- 提取 boundary
local boundary = content_type:match("boundary=(.+)$")
if not boundary then return nil, "no boundary" end
boundary = "--" .. boundary
-- 簡易分割解析 (生產環境建議使用 lua-resty-upload 庫)
local parts = {}
local start_pos = 1
while true do
local part_start = body_data:find(boundary, start_pos, true)
if not part_start then break end
local part_end = body_data:find(boundary, part_start + #boundary, true)
if not part_end then break end
local part = body_data:sub(part_start + #boundary + 2, part_end - 4) -- 去除 \r\n
table.insert(parts, part)
start_pos = part_end
end
local args, file_args = {}, {}
for _, part in ipairs(parts) do
local headers_block, value = part:match("^(.-)\r\n\r\n(.+)$")
if headers_block and value then
local name = headers_block:match('name="([^"]+)"')
local filename = headers_block:match('filename="([^"]+)"')
if name then
if filename then
file_args[name] = { filename = filename, content = value }
else
args[name] = value
end
end
end
end
return args, file_args
end
-- 使用示例
local get_args = ngx.req.get_uri_args()
local post_args, files = parse_multipart_body()
local all_args = post_args or get_args
手寫解析器易出錯且效能不佳。強烈建議使用 OpenResty 生態的成熟庫:
local upload = require "resty.upload"local reqargs = require "resty.reqargs"ngx.ctx 避免重複解析。掌握 Nginx+Lua 參數處理機制,能構建高效能、靈活的 API 網關與 Web 應用防火牆 (WAF) 邏輯。