OpenResty获取参数的API

一、前言:OpenResty 提供了哪些"官方"参数获取 API?

很多初学者在 OpenResty 中获取请求参数时,要么记混方法,要么漏掉关键步骤(如未读 body),导致 nil 或空值。

其实,OpenResty(通过 ngx_lua 模块)提供了一套标准、高效的 API 来获取各类请求数据

掌握这些 API,你就能精准、安全、高效地提取所需信息。

本文将系统梳理所有核心 API,并附上使用示例与注意事项,助你一次写对!


二、核心 API 速览表

参数类型 API 方法 是否需预读 body 返回类型
请求方法 ngx.req.get_method() ❌ 否 string ("GET", "POST"...)
URI 路径 ngx.var.uri ❌ 否 string (/api/user)
GET 参数 ngx.req.get_uri_args() ❌ 否 table
请求头 ngx.req.get_headers() ❌ 否 table
POST 表单 ngx.req.get_post_args() ✅ 是 table
原始 Body ngx.req.get_body_data() ✅ 是 string / nil
Body 文件路径 ngx.req.get_body_file() ✅ 是 string / nil

🔑 黄金法则只要涉及请求体(body),必须先调用 ngx.req.read_body()


三、详细 API 使用指南

3.1 获取请求方法与 URI

Lua 复制代码
local method = ngx.req.get_method()  -- "GET", "POST", "PUT"...
local uri = ngx.var.uri              -- "/api/v1/user/123"

ngx.say("Method: ", method)
ngx.say("URI: ", uri)

💡 ngx.var.* 可访问所有 Nginx 内置变量(如 $remote_addr, $http_user_agent)。


3.2 获取 GET 参数(Query String)

Lua 复制代码
-- 请求: /search?q=openresty&page=2
local args = ngx.req.get_uri_args()

-- 安全访问
local q = args.q or ""
local page = tonumber(args.page) or 1

ngx.say("Search for: ", q, " on page ", page)

特点

  • 自动 URL 解码(%E4%B8%AD
  • 多值参数返回数组:?tag=a&tag=b{a, b}

3.3 获取请求头(Headers)

Lua 复制代码
local headers = ngx.req.get_headers()

-- 单个 Header(自动转小写)
local auth = headers.authorization  -- 注意是小写!
local ua = headers["user-agent"]

-- 遍历所有 Header
for name, value in pairs(headers) do
    ngx.log(ngx.INFO, "Header: ", name, " = ", tostring(value))
end

⚠️ 注意

  • Header 名自动转为小写
  • 多值 Header(如 Set-Cookie)返回 table

3.4 获取 POST 表单数据(x-www-form-urlencoded)

Lua 复制代码
-- 必须先读取 body!
ngx.req.read_body()

local post_args = ngx.req.get_post_args()

if not post_args then
    ngx.exit(400)
end

local username = post_args.username
local password = post_args.password

ngx.say("Login user: ", username or "unknown")

适用 Content-Typeapplication/x-www-form-urlencoded


3.5 获取原始请求体(Raw Body)

适用于 JSON、XML、Protobuf 等自定义格式。

Lua 复制代码
ngx.req.read_body()

-- 尝试从内存获取
local body = ngx.req.get_body_data()

-- 若 body 较大,会存入临时文件
if not body then
    local file_path = ngx.req.get_body_file()
    if file_path then
        local file = io.open(file_path, "r")
        body = file:read("*a")
        file:close()
    end
end

if body then
    ngx.say("Body length: ", #body)
else
    ngx.say("No body")
end

📌 触发文件存储的条件 :body 大小 > client_body_buffer_size(默认 8k~16k)


3.6 解析 JSON(配合 cjson)

虽然 cjson 不是 OpenResty 内置 API,但几乎必用:

Lua 复制代码
ngx.req.read_body()
local body = ngx.req.get_body_data()
if not body then ngx.exit(400) end

local cjson = require "cjson.safe"
local data, err = cjson.decode(body)

if not data then
    ngx.log(ngx.ERR, "JSON decode failed: ", err)
    ngx.exit(400)
end

ngx.say("User ID: ", data.userId)

强烈建议使用 cjson.safe,避免解析错误导致 worker 崩溃。


四、关键注意事项(避坑必看!)

⚠️ 1. ngx.req.read_body() 是阻塞操作

  • 会暂停当前请求直到 body 读完
  • 不要在高并发下处理超大文件(考虑流式读取)

⚠️ 2. get_post_args() 无法解析 JSON

  • 它只处理 key=value&key2=value2 格式
  • JSON 必须用 get_body_data() + cjson

⚠️ 3. 多值参数处理

Lua 复制代码
-- ?id=1&id=2
local ids = ngx.req.get_uri_args().id
if type(ids) == "table" then
    for _, id in ipairs(ids) do ... end
else
    -- 单值
end

⚠️ 4. 内存限制

  • 默认最大 body 为 client_max_body_size(通常 1M)
  • 超出会返回 413 错误

五、最佳实践:封装统一参数获取函数

Lua 复制代码
local _M = {}

function _M.get_all_params()
    local method = ngx.req.get_method()
    local params = {}

    if method == "GET" then
        params = ngx.req.get_uri_args()
    else
        ngx.req.read_body()
        local content_type = ngx.req.get_headers()["content-type"] or ""
        
        if content_type:find("application/json", 1, true) then
            local body = ngx.req.get_body_data()
            if body then
                local cjson = require "cjson.safe"
                params = cjson.decode(body) or {}
            end
        else
            params = ngx.req.get_post_args() or {}
        end
    end

    return params
end

return _M

在配置中使用:

Lua 复制代码
access_by_lua_block {
    local param_utils = require "param_utils"
    local params = param_utils.get_all_params()
    -- ...
}

六、结语

感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!

相关推荐
小小龙学IT10 小时前
C++20 协程深度解析:从原理到高性能异步框架实战
junit·c++20
IT策士1 天前
Redis 从入门到精通:事务与 Lua 脚本
redis·junit·lua
北极星日淘1 天前
日淘平台优惠券系统的设计:从规则引擎到防超领
junit
慧都小妮子1 天前
不想频繁改 PLC?用 DeviceXPlorer Lua 脚本把产线业务逻辑放到 OPC Server 层
java·junit·lua·takebishi·dxpserver·设备数据采集软件·opc server
闪电悠米3 天前
黑马点评-Redis 消息队列-03_stream_consumer_group
开发语言·数据库·redis·分布式·缓存·junit·lua
闪电悠米3 天前
黑马点评-Redis 消息队列-04_stream_seckill_order
数据库·redis·分布式·缓存·oracle·junit·lua
摇滚侠3 天前
Spring 零基础入门到进阶 单元测试 JUnit 52-60
spring·junit·单元测试
呦呦鹿鸣Rzh3 天前
Redis Lua 脚本:从入门到避坑指南
redis·junit·lua
闪电悠米4 天前
黑马点评-Redis 消息队列-01_why_redis_mq
java·数据库·spring boot·redis·缓存·junit·消息队列
ourenjiang5 天前
【测试框架Junit】强制终止JVM进程
jvm·junit