# Rest API supported in OpenWrt

**URL:** <https://forum.openwrt.org/t/rest-api-supported-in-openwrt/17210>\
**Category:** For Developers\
**Created:** [July 18, 2018, 5:25am UTC](https://forum.openwrt.org/t/rest-api-supported-in-openwrt/17210 "2018-07-18T05:25:19Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![joeylee](https://forum.openwrt.org/letter_avatar/joeylee/32/5_5575768a8748004e209b776fc1b2916d.png) [@joeylee](https://forum.openwrt.org/u/joeylee)\
**Post date:** [July 18, 2018, 5:25am UTC](https://forum.openwrt.org/t/rest-api-supported-in-openwrt/17210/1 "2018-07-18T05:25:19Z")

</div>

Hello All,

I'm studying the feasibility to support REST API in OpenWrt  
I would like to make some REST API to reach the feature like:

1. Software image update: Using REST API to update image through network, need to enable TFTP server at same time??
2. Board Management: Run reboot/restart and reset to factory default by REST API

How to implement this API under built-in web server(uhttpd) or other web server daemon?  
Or Is there any existing packages have solution to support this in OpenWrt  
Thanks a lot.

---

<div class="post-metadata">

**Author:** ![jow](https://forum.openwrt.org/user_avatar/forum.openwrt.org/jow/32/13_2.png) [@jow](https://forum.openwrt.org/u/jow)\
**Post date:** [July 18, 2018, 6:06am UTC](https://forum.openwrt.org/t/rest-api-supported-in-openwrt/17210/2 "2018-07-18T06:06:23Z")

</div>

Write a CGI program triggering the required actions.

---

<div class="post-metadata">

**Author:** ![phatpaul](https://forum.openwrt.org/letter_avatar/phatpaul/32/5_5575768a8748004e209b776fc1b2916d.png) [@phatpaul](https://forum.openwrt.org/u/phatpaul)\
**Post date:** [July 30, 2018, 2:07pm UTC](https://forum.openwrt.org/t/rest-api-supported-in-openwrt/17210/3 "2018-07-30T14:07:57Z")

</div>

I'm in the same boat joeylee. I can't seem to find a complete example anywhere of using uhttpd for REST. After struggling for a couple days with uhttpd, I'm going to try a different webserver. Maybe lighttpd or nginx.

Here's what I tried:  
[https://bits.mdminhazulhaque.io/openwrt/run-custom-lua-script-as-cgi-with-uhttpd.html](https://bits.mdminhazulhaque.io/openwrt/run-custom-lua-script-as-cgi-with-uhttpd.html)  
gives an example of running a cgi program with uhttpd.  
It is using the built-in Lua interpreter (running inside uhttpd) as opposed to the cgi-bin method.

I'm able to serve static content and generate dynamic content so far.  
But I'm struggling to parse the parameters given in a GET or POST  
(i.e. [http://192.168.1.70/lua/?email=blah](http://192.168.1.70/lua/?email=blah) or the same email=blah POSTed with a form)

> <https://stackoverflow.com/questions/34346499/how-to-read-post-from-lua-on-openwrt-uhttpd>

  
gives some clues and I spent some time dissecting how Luci is doing it.

Supposedly, you should create a "request", and use that to parse the parameters.

local req = luci.http.Request(  
renv, recv, luci.ltn12.sink.file(io.stderr)  
)

and then access the parsed parameters as

req.formvalue("email")

This method is working for parameters passed in a GET (i.e. [http://192.168.1.70/lua/?email=blah](http://192.168.1.70/lua/?email=blah))  
But not working when passed in a POST (seems req is empty).

---

<div class="post-metadata">

**Author:** ![jow](https://forum.openwrt.org/user_avatar/forum.openwrt.org/jow/32/13_2.png) [@jow](https://forum.openwrt.org/u/jow)\
**Post date:** [July 30, 2018, 2:43pm UTC](https://forum.openwrt.org/t/rest-api-supported-in-openwrt/17210/4 "2018-07-30T14:43:13Z")

</div>

In a traditional CGI application you do need to take care of parsing parameters and POST bodies yourself. URL query parameters are passed via the `QUERY_STRING` variable (`os.getenv("QUERY_STRING")`) while POST bodies in either `application/x-www-form-urlencoded` or `multipart/form-data` format are fed to the invoked program via stdin.

This has nothing to do with uhttpd or lighttpd but with the basic operation principle of plain CGI. Usually you have HTTP parsing support directly built into the language (e.g. with PHP) or it is available as separate library (e.g. `CGI.pm` for Perl).

The simplest uhttpd embedded Lua application which does not built upon LuCI but uses LuCI's HTTP abstraction library is this:

`root@OpenWrt:~# cat /root/simple-app.lua`

```
require "luci.http"

function handle_request(env)
	local renv = {
		CONTENT_LENGTH = env.CONTENT_LENGTH,
		CONTENT_TYPE = env.CONTENT_TYPE,
		REQUEST_METHOD = env.REQUEST_METHOD,
		REQUEST_URI = env.REQUEST_URI,
		PATH_INFO	= env.PATH_INFO,
		SCRIPT_NAME = env.SCRIPT_NAME:gsub("/+$", ""),
		SCRIPT_FILENAME = env.SCRIPT_NAME,
		SERVER_PROTOCOL = env.SERVER_PROTOCOL,
		QUERY_STRING = env.QUERY_STRING
	}

	local k, v
	for k, v in pairs(env.headers) do
		k = k:upper():gsub("%-", "_")
		renv["HTTP_" .. k] = v
	end

	local len = tonumber(env.CONTENT_LENGTH) or 0
	local function recv()
		if len > 0 then
			local rlen, rbuf = uhttpd.recv(4096)
			if rlen >= 0 then
				len = len - rlen
				return rbuf
			end
		end
		return nil
	end

	local send = uhttpd.send
	local req = luci.http.Request(renv, recv, function(s) io.stderr:write(s) end)

	send("Status: 200 OK\r\n")
	send("Content-Type: text/html\r\n\r\n")

	send("<h1>Headers</h1>\n")
	for k, v in pairs(env.headers) do
		send(string.format("<strong>%s</strong>: %s<br>\n", k, v))
	end

	send("<h1>Environment</h1>\n")
	for k, v in pairs(env) do
		if type(v) == "string" then
			send(string.format("<code>%s=%s</code><br>\n", k, v))
		end
	end

	send("<h1>Parameters</h1>\n")
	for k, v in pairs(req:formvalue()) do -- invoking :formvalue() without name will return a table of all args
		send(string.format("<strong>%s</strong>: %s<br>\n", k, v))
	end
end

```

Register it in uhttpd using

```
	option lua_prefix '/app'
	option lua_handler '/root/simple-app.lua'

```

When you invoke uhttpd from the outside, e.g. using `curl -F foo=bar -F bar=baz -v http://192.168.1.1/app`, you should see a response like this:

```
* Hostname was NOT found in DNS cache
* Trying 192.168.1.1...
* Connected to 192.168.1.1 (192.168.1.1) port 80 (#0)
> POST /app/ HTTP/1.1
> User-Agent: curl/7.38.0
> Host: 192.168.1.1
> Accept: */*
> Content-Length: 236
> Expect: 100-continue
> Content-Type: multipart/form-data; boundary=------------------------0773a465fc34530a
> 
< HTTP/1.1 100 Continue
< HTTP/1.1 200 OK
< Connection: close
< Transfer-Encoding: chunked
< Content-Type: text/html
< 
<h1>Headers</h1>
<strong>host</strong>: 192.168.1.1<br>
<strong>expect</strong>: 100-continue<br>
<strong>URL</strong>: /app/<br>
<strong>user-agent</strong>: curl/7.38.0<br>
<strong>content-type</strong>: multipart/form-data; boundary=------------------------0773a465fc34530a<br>
<strong>content-length</strong>: 236<br>
<strong>accept</strong>: */*<br>
<h1>Environment</h1>
<code>SERVER_NAME=192.168.1.1</code><br>
<code>SCRIPT_NAME=/app</code><br>
<code>QUERY_STRING=</code><br>
<code>SERVER_ADDR=192.168.1.1</code><br>
<code>GATEWAY_INTERFACE=CGI/1.1</code><br>
<code>REMOTE_ADDR=192.168.1.7</code><br>
<code>CONTENT_LENGTH=236</code><br>
<code>SERVER_PORT=80</code><br>
<code>SCRIPT_FILENAME=/root/simple-app.lua</code><br>
<code>REQUEST_URI=/app/</code><br>
<code>SERVER_PROTOCOL=HTTP/1.1</code><br>
<code>REMOTE_HOST=192.168.1.7</code><br>
<code>REDIRECT_STATUS=200</code><br>
<code>SERVER_SOFTWARE=uhttpd</code><br>
<code>HTTP_HOST=192.168.1.1</code><br>
<code>REMOTE_PORT=40280</code><br>
<code>HTTP_ACCEPT=*/*</code><br>
<code>PATH_INFO=/</code><br>
<code>HTTP_USER_AGENT=curl/7.38.0</code><br>
<code>CONTENT_TYPE=multipart/form-data; boundary=------------------------0773a465fc34530a</code><br>
<code>REQUEST_METHOD=POST</code><br>
<h1>Parameters</h1>
<strong>bar</strong>: baz<br>
<strong>foo</strong>: bar<br>
* Closing connection 0
jow@jow:~$ 

```

---

<div class="post-metadata">

**Author:** ![jow](https://forum.openwrt.org/user_avatar/forum.openwrt.org/jow/32/13_2.png) [@jow](https://forum.openwrt.org/u/jow)\
**Post date:** [July 30, 2018, 2:51pm UTC](https://forum.openwrt.org/t/rest-api-supported-in-openwrt/17210/5 "2018-07-30T14:51:09Z")

</div>

Given these basic building blocks you can now start doing RESTy stuff:

- `env.PATH_INFO` refers to the relative path portion after `option lua_prefix` (`/app` in our example)
- `env.REQUEST_METHOD` contains the HTTP method used for requesting, e.g. `GET`, `POST`, `PUT` etc.
- to set the response HTTP status, use the `Status` pseudo header: `send("Status: 503 Server Error\r\n\r\n")`
- to set the response content type, send a `Content-Type` header: `send("Content-Type: application/json; charset=UTF-8\r\n")`
- to terminate the header block and start with content, send a sole `\r\n` or end the last header with `\r\n\r\n`: `send("X-Foobar: 1\r\n\r\n")` or `send("\r\n")`
- to quickly serialize JSON, you can use [LuCI's jsonc library binding](http://htmlpreview.github.io/?http://raw.githubusercontent.com/openwrt/luci/master/documentation/api/modules/luci.jsonc.html): `require "luci.jsonc"; send(luci.jsonc.stringify({ some = { complex = { data = "structure", foo = true, bar = { 1, 2, 3 } } } }))`

---

<div class="post-metadata">

**Author:** ![phatpaul](https://forum.openwrt.org/letter_avatar/phatpaul/32/5_5575768a8748004e209b776fc1b2916d.png) [@phatpaul](https://forum.openwrt.org/u/phatpaul)\
**Post date:** [July 30, 2018, 3:38pm UTC](https://forum.openwrt.org/t/rest-api-supported-in-openwrt/17210/6 "2018-07-30T15:38:49Z")

</div>

Thanks jow,

That got it working for me! I was not familiar with Lua, so i mistakenly tried to use req.formvalue("email") instead of req:formvalue("email").

I also tested your example with a form post and it correctly parses.

```
send("Status: 200 OK\r\n")
send("Content-Type: text/html\r\n\r\n")
uhttpd.send("Content-Type: text/html\r\n\r\n")
uhttpd.send("<!DOCTYPE html>")
uhttpd.send("<html>\n")
uhttpd.send("<body>\n")
uhttpd.send("<form action=\"/lua/\" method=\"post\">\n")
uhttpd.send(" <input type=\"email\" name=\"email\" placeholder=\"email@example.com\" />\n")
uhttpd.send(" <input type=\"submit\" value=\"submit\" />\n")
uhttpd.send("</form>\n")
uhttpd.send("</body>\n")
uhttpd.send("</html>\n")

```

An example like you provided helps us newbies tremendously. Please consider adding it to the documentation somewhere.

---

<div class="post-metadata">

**Author:** ![jow](https://forum.openwrt.org/user_avatar/forum.openwrt.org/jow/32/13_2.png) [@jow](https://forum.openwrt.org/u/jow)\
**Post date:** [July 30, 2018, 4:37pm UTC](https://forum.openwrt.org/t/rest-api-supported-in-openwrt/17210/7 "2018-07-30T16:37:39Z")

</div>

Btw, Lua has some interesting string quoting possibilities which help to unclutter your code. You can wrap your strings in `[[` and `]]` which will act like double quotes. Using these, your example above would become:

```
uhttpd.send("Status: 200 OK\r\n")
uhttpd.send("Content-Type: text/html\r\n\r\n")
uhttpd.send([[
<!DOCTYPE html>
<html>
  <body>
    <form action="/lua/" method="post">
      <input type="email" name="email" placeholder="email@example.com" />
      <input type="submit" value="submit" />
     </form>
  </body>
</html>
]])

```

---

<div class="post-metadata">

**Author:** ![aslammuhammed](https://forum.openwrt.org/letter_avatar/aslammuhammed/32/5_5575768a8748004e209b776fc1b2916d.png) [@aslammuhammed](https://forum.openwrt.org/u/aslammuhammed)\
**Post date:** [July 2, 2020, 3:27pm UTC](https://forum.openwrt.org/t/rest-api-supported-in-openwrt/17210/9 "2020-07-02T15:27:40Z")

</div>

@jow How can i define multiple API's using this implementation?

---

<div class="post-metadata">

**Author:** ![aslammuhammed](https://forum.openwrt.org/letter_avatar/aslammuhammed/32/5_5575768a8748004e209b776fc1b2916d.png) [@aslammuhammed](https://forum.openwrt.org/u/aslammuhammed)\
**Post date:** [July 7, 2020, 10:55am UTC](https://forum.openwrt.org/t/rest-api-supported-in-openwrt/17210/10 "2020-07-07T10:55:53Z")

</div>

how can i define a post API using env.REQUEST\_METHOD. I am new to this please help.

---

<div class="post-metadata">

**Author:** ![Bob2](https://forum.openwrt.org/letter_avatar/bob2/32/5_5575768a8748004e209b776fc1b2916d.png) [@Bob2](https://forum.openwrt.org/u/Bob2)\
**Post date:** [February 12, 2022, 4:54am UTC](https://forum.openwrt.org/t/rest-api-supported-in-openwrt/17210/11 "2022-02-12T04:54:08Z")

</div>

Thanks for your help! if POST bodies is application/json, it fails to get data by req:formvalue(). Is there any function to deal with request conntent type with appllicaiton/json?
