前几节,HTTP服务器和客户端交流采用的都是GET
方法。客户端向服务器请求一个文件,或者发送一个button附带的固定信息。
除了GET
方法外,常用的还有
- POST
客户端向服务器发送信息,比如提交一个表单。
- PUT
向服务器上传文件
- DELETE
删除服务器上的文件。
这一节主要讲一下POST方法,代码采用HTTP 3
介绍的WEB 框架。
我们做一个支持POST方法的HTTP WebServer,实现web下的lua控制台。
- * 在手机和平板上,用浏览器远程调试nodemcu*
接上节
Step 7
我们首先写一个包含表单的HTML文件,form2.html
,作为服务器的主页。
<html>
<head>
<title> Remote Debug </title>
</head>
<body>
<h1> Remote Debug Your Nodemcu</h1>
<form method="POST">
<p>
</br>
Input your LUA command here:</br>
<textarea name="comments" rows="2" cols="70"></textarea></br>
</br>
Submit your command</br>
<input type="submit" /></br>
</br>
Response</br>
</p>
</form>
<iframe name="content_frame" width="500" height="100" src="print.txt" frameborder=0></iframe></br>
</body>
</html>
<html>
,<head>
,<body>
标签已经讲过,我们来看<form>
标签,代码算空行第9行<form method="POST">
定义了from的方法是POST
,用于客户端向服务器提交数据。<form></form>
里有一个完整的的<p></p>
<p></p>
里定义了两个对象:<texterea>
和<input>
- 13行,
<texterea>
定义一个输入文本框,名称为comments
,POST提交后,文本框的输入内容...
,会以comments=...
的形式发送给服务器。
rows
定义了文本框的高度,两行。cols
定义宽度。</br>
代表空一行文本框长这样:
- 17行,
<input type="submit" />
定义一个提交按钮,,用来提交form
对象中所有输入的信息
- 13行,
<iframe>
标签可以在html
里插入视频,图像,html等文件。这里我们插入了一个txt文档print.txt
, 方法为src = "print.txt"
,同时还定义了名称name
,宽度width
和高度height
print.txt
用来显示nodemcu执行后的响应信息
Step 8
有了主页,我们写一个web server,名称为form2.lua
。内容上和上一节介绍的web server比较类似,只是传送文件部分用了网上最流行的写法,可靠性应该更高一些。
SSID = ""
password = ""
pin = 4
gpio.mode(pin, gpio.OUTPUT)
wifi.setmode(wifi.STATION)
wifi.sta.config(SSID,password)
wifi.sta.connect()
tmr.alarm(1, 1000, 1, function()
if wifi.sta.getip() == nil then
print("Connecting...")
else
tmr.stop(1)
print("Connected, IP is "..wifi.sta.getip())
end
end)
file.open("print.txt","w+")
file.write('Command:')
file.close()
srv=net.createServer(net.TCP,30)
srv:listen(80,function(conn)
-- save node.output to print.txt
function s_output(str)
if(conn~=nil) then
file.open("print.txt","a+")
file.write(str)
file.close()
end
end
node.output(s_output, 1)
conn:on("receive", function(conn,payload)
-- parse Header
local _, _, vars = string.find(payload, "[A-Z]+ /(.+) HTTP");
local _, _, method = string.find(payload, "([A-Z]+) /.* HTTP");
local filename = nil
if (method =="GET") then
if (vars == nil) then filename = "form2.html"
elseif (vars ~= nil) then filename = vars
end
elseif (method =="POST") then
local _, _, comments =
string.find(payload,"comments=(.*)")
local symbol = {
['%2B'] = '+',
['%2F'] = '/',
['%28'] = '(',
['%29'] = ')',
['%22'] = '"',
['%27'] = "'"
}
-- replace symbol in comments
if (comments ~= nil)then
for sym1,sym2 in pairs(symbol) do
com_sub,n = string.gsub(comments,'%'..sym1,sym2)
if n~=0 then comments = com_sub end
end
-- execute comments
print(comments)
node.input(comments)
end
-- redirect
filename = "form2.html"
end
-- send file
local pos = 0;
local function send ()
file.open(filename,"r")
if (file.seek("set",pos) == nil ) then
conn:close()
collectgarbage()
else
local buf2 = file.read(1024)
conn:send(buf2)
pos = pos +1024
end
file.close()
-- initialize print.txt
if (filename == "print.txt") then
file.open("print.txt","w+")
file.write('Command:')
file.close()
end
end
conn:on("sent", send)
send()
end )
end )
<*_>
很多代码和http 3
文档中的几乎一样,连接路由器的代码在前面的文档也可以找到。我们只介绍关键的代码:
- 代码算空行,第19到21行,file.open(),file.write(),file.close
打开print.txt
文件,初始化,然后关闭
- 26行,function s_output(str)
定义一个函数,把执行后的响应信息,存到print.txt
文档中
- 第27行,如果连接conn
不为空,即conn 没有close.
- 第28行,以追加方式a+
打开文件,字符写到文档末尾处
- 第29,30行,写字符串并关闭
- 37-47行,解析http head,详情见http 3
- 41行,对于get
方法
- 42行,如果刚连接,客户端没有请求文件,定位到主页form2.html
- 43行,如果客户端请求文件,就定位到要发送的文件
- 45行,如果是post
方法,46行 ,解析出commets
由于在html文本框输入的commets,遇到+
,/
,()
,""
会转换成%
开头的代码,所以要转换一下
- 49-50行,定义特殊符号转换表格
- 58-62行,for循环实现转换。
- 59行,对所有特殊字符循环个遍
- 60行,把特殊字符替换成本来的样子,如果恰巧commets没有该特殊字符,n=0
- 61行,如果comments里存在循环到的特殊字符,就把comments替换,没有就不替换。否则会把其它特殊字符替换后的结果覆盖掉。
- 66行,替换后的comments交给nodemcu执行
- 74-92行,发送文件,详情见
http 3
- 89-92行,如果文件名是
print.txt
, 发送一部分(1024byte)后,就重新初始化,
- 这部分写得不好,最好是在文件发送完后再初始化。不过一般print.txt不会超过1024字符,通常没有问题
这段WebServer代码的思路
是在主页from2.html
里嵌入一个txt文档print.txt
,显示nodemcu的响应信息
- 当客户端post信息后。服务器就把代码发给nodemcu执行(nodemcu.input()),然后响应信息存到print.txt
- 同时,向客户端发送主页from2.html
, 等到主页向客户端传送完,客户端向服务器请求get print.txt
, 这时nodemcu已经执行完代码,print.txt已经存了响应信息。
- print.txt 被传送完后(1024byte),会被初始化。等待下一条命令。
要说明的问题
- 文本框输入命令不要超过1024byte,否则可能出现问题。即便使用串口调试,命令行太长也有报错的可能。
- 通过该界面可以上传文件,或者删除。可以用javascript 实现分行上传文件。ESPlore也是用分行上传的方式实现的。
Step 9
- 把4个文件
wf4.lua
,from2.html
,print.txt
,favcion.ico
上传,
- 注意更新
wf4.lua
中的SSID
和password
。 print.txt
只要不是空文件就行,因为每次执行wf4.lua
都会初始化。favcion.ico
是服务器的图表文件。- 这四个文件和这篇文档一起上传到贴论坛里了。
- 注意更新
dofile('wf4.lua')
,显示nodemcu
的IP- 在浏览器的地址栏输入nodemcu的
IP
,不要使用360浏览器 - 在文本框输入lua命令行,点击
提交
按钮,下方显示响应信息。 - 界面如下图: