主题
http — HTTP 客户端
模块名:http
发起 HTTP/HTTPS 的 GET/POST 请求,支持同步和异步两种方式。同步请求会阻塞当前 Lua 执行直到收到响应或超时(内部固定 15 秒);异步请求在后台发起,结果通过消息队列回到 Lua 主循环执行回调,不阻塞脚本。
多网卡机型可以在每次请求时指定出口网卡 IP,用来强制走以太网或走 WiFi。
函数总览
库级函数
| 函数 | 作用 |
|---|---|
http.creat(string, string, ...) | 创建 HTTP 客户端对象,可预设 URL 和出口网卡 |
http.get(string, string, ...) | 直接传 URL 的同步 GET |
http.post(string, string, ...) | 直接传 URL 的同步 POST |
对象方法
| 方法 | 作用 |
|---|---|
client:add_head | 添加或删除请求头 |
client:get | 用对象已配置的 URL 和头部做同步 GET |
client:post | 同步 POST |
client:get_asy | 异步 GET,结果走回调 |
client:post_asy | 异步 POST,结果走回调 |
本模块不注册任何常量。
http.creat
lua
http.creat([url, ip]) --> client创建 HTTP 客户端对象。两个参数都可省略。
参数
| # | 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|
| 1 | url | string | 否 | — | 请求地址,可在此预设,也可在 get_asy/post_asy 时再传 |
| 2 | ip | string | 否 | — | 出口网卡的本地 IP,多网卡环境用于指定从哪块网卡发起 |
返回值
| 类型 | 说明 |
|---|---|
| userdata | HTTP 客户端对象。必须用变量接住,否则可能被 GC 回收 |
示例
lua
local client = http.creat("http://api.example.com/data")http.get
lua
http.get(url, [ip]) --> string | nil, string同步 GET,不需要先创建对象。
参数
| # | 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|
| 1 | url | string | 是 | — | 请求地址 |
| 2 | ip | string | 否 | — | 出口网卡 IP |
返回值
| 类型 | 说明 |
|---|---|
| string | 成功返回响应体;服务端无返回体时是空串 "" |
| nil, string | 失败返回 nil 和错误信息 "HTTP GET request failed" |
示例
lua
local data, err = http.get("http://api.example.com/data")
if not data then print("请求失败:", err) end说明
- 这种简单模式每次调用都临时创建客户端,无法带自定义请求头。需要自定义头部时用
http.creat+client:get。
http.post
lua
http.post(url, body, [ip]) --> string | nil, string同步 POST,不需要先创建对象。
参数
| # | 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|
| 1 | url | string | 是 | — | 请求地址 |
| 2 | body | string | 是 | — | 请求体,可为二进制 |
| 3 | ip | string | 否 | — | 出口网卡 IP |
返回值
| 类型 | 说明 |
|---|---|
| string | 成功返回响应体;无返回体时是空串 "" |
| nil, string | 失败返回 nil 和 "HTTP POST request failed" |
client:add_head
lua
client:add_head(name, value)添加或覆盖一个请求头。
参数
| # | 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|
| 1 | name | string | 是 | — | 头部名称 |
| 2 | value | string | 是 | — | 头部值。传空字符串会删除该头部 |
返回值
无返回值。
示例
lua
client:add_head("Content-Type", "application/json")
client:add_head("Authorization", "Bearer " .. token)
client:add_head("User-Agent", "") -- 删除该头部client:get
lua
client:get([ip]) --> string | nil, string用对象已配置的 URL 和请求头做同步 GET。
参数
| # | 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|
| 1 | ip | string | 否 | — | 运行时切换出口网卡 IP |
返回值
同 http.get。
client:post
lua
client:post(body, [ip]) --> string | nil, string用对象已配置的 URL 和请求头做同步 POST。
参数
| # | 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|
| 1 | body | string | 是 | — | 请求体 |
| 2 | ip | string | 否 | — | 运行时切换出口网卡 IP |
返回值
同 http.post。
client:get_asy
lua
client:get_asy(url, callback, [ip])异步 GET。不阻塞脚本,结果通过回调返回,回调在 Lua 主循环中执行。
参数
| # | 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|
| 1 | url | string | 是 | — | 请求地址(不使用 creat 时预设的 URL) |
| 2 | callback | function | 是 | — | 结果回调,签名见下。类型不是 function 会直接报错 |
| 3 | ip | string | 否 | — | 出口网卡 IP |
回调签名
lua
function(errcode, data, datalen)| 参数 | 类型 | 说明 |
|---|---|---|
errcode | integer | 错误码,0 表示成功 |
data | string | 响应数据 |
datalen | integer | 响应数据长度 |
返回值
无返回值。
示例
lua
local client = http.creat()
client:get_asy("http://api.example.com/data", function(errcode, data, datalen)
if errcode == 0 then
print("收到", datalen, "字节:", data)
else
print("请求失败,错误码", errcode)
end
end)client:post_asy
lua
client:post_asy(url, body, callback, [ip])异步 POST。
参数
| # | 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|
| 1 | url | string | 是 | — | 请求地址 |
| 2 | body | string | 是 | — | 请求体 |
| 3 | callback | function | 是 | — | 结果回调,签名同 get_asy |
| 4 | ip | string | 否 | — | 出口网卡 IP |
返回值
无返回值。
示例
lua
local client = http.creat()
client:add_head("Content-Type", "application/json")
client:post_asy("http://api.example.com/upload",
json.encode({ temp = plc.df(100) }),
function(errcode, data)
print(errcode == 0 and "上报成功" or "上报失败")
end)例程
每个例程都是可直接下载运行的完整脚本:注册完定时器后脚本就结束,固件自动进入消息泵。 API 只用本页已文档化的函数;所有例程遵循统一模板「场景 / 完整代码 / 要点 / 验证方法 / 注意事项」。
例程 1:开机拉一次云端阈值到本地 D 寄存器
适用:LP3201 / LP3202 / LP3203 / LP3222 / LP3232 / LP3288 / 软PLC模拟器 | 依赖模块:http json timer plc | 难度:★☆☆
场景:阈值、量程这类参数写在服务端,设备每次上电拉一次写进 D200 / D201,梯形图照常比大小 —— 参数改一次服务端即可,不用重新下载 Lua。拉失败就保留上次的值,绝不用半截数据覆盖。
完整代码(可直接下载运行):
lua
-- 上电 5 秒后拉一次配置;失败或格式不对都不改写本地 D
local CFG = "http://192.168.1.10:8080/api/config/" .. plc.plc_name()
httpc = http.creat(CFG) -- 必须长期持有,不要用完即弃的局部对象
httpc:add_head("Accept", "application/json")
timer.start(5000, function()
local body, err = httpc:get()
if not body then
print("拉配置失败:", err, ",保持 D200/D201 原值")
return
end
local ok, cfg = pcall(json.decode, body)
if not (ok and type(cfg) == "table") then
print("返回不是 JSON:", body)
return
end
-- 现场约定温度单位 0.1℃,用整数存 D,梯形图里直接 CMP 比较
if cfg.temp_hi ~= nil then plc.d(200, tonumber(cfg.temp_hi) or plc.d(200)) end
if cfg.temp_lo ~= nil then plc.d(201, tonumber(cfg.temp_lo) or plc.d(201)) end
print("配置已生效 hi=", plc.d(200), "lo=", plc.d(201))
end, true) -- true = 一次性定时器要点:
httpc:get()用creat时预设的 URL,能带自定义头部;http.get(url)那种一次性写法不行。- 同步接口会阻塞整个事件循环最长 15 秒(网络不好时的内部固定超时),所以只用在上电一次性任务上,周期任务看例程 3。
- 第三参
true让定时器只跑一次,跑完自动释放,不用stop()。 json.decode必须pcall包住:服务端返回 HTML 错误页(HTTP 200 但内容不是 JSON)是最常见的坑,pcall才能稳住。tonumber(cfg.temp_hi) or plc.d(200):字段缺失或类型不对时保持原值,宁可不更新也不要写进 0。
验证方法:
- PC 上准备一个返回
{"temp_hi":350,"temp_lo":100}的接口(改例程里的 IP/端口即可)。 - 下载脚本、重启设备,5 秒后日志打印
配置已生效 hi=350 lo=100。 - 上位机「在线监视」里看 D200=350、D201=100。
- 关掉服务端再重启:日志打印「拉配置失败」,D200 / D201 保持上一次的值不被清空。
注意事项:
- 服务端返回 HTML 错误页时
body非 nil(HTTP 200 也照样成功返回),所以要pcall(json.decode)后再看字段,不能只看有没有返回值。 - 同步
get/post会卡住整个事件循环,把它放进短周期定时器会让其它任务一起卡死。 - 客户端对象被 GC 回收会导致请求途中失效,例程里用全局变量持有。
其余例程(各自一页)
- HTTP 客户端例程 2:事件触发的同步上报(要服务端回执时才值得同步)
- HTTP 客户端例程 3:周期异步上报(推荐做法)+ 失败退避
- HTTP 客户端例程 4:现场常用脚本
http.LPLUA(原文)
注意事项
- 同步
get/post会阻塞整个 Lua 事件循环,网络较慢时定时器和其它回调都会停止推进。在 timer 回调里应统一使用异步接口。 - 异步回调在主循环里执行,不是独立线程,回调内不要做长阻塞操作。
- 客户端对象必须用变量长期持有,否则请求返回前对象可能被 GC 回收。
- 简单模式
http.get(url)无法携带请求头,需要认证的接口应使用对象模式。