I'm trying to send a 0
or a 1
to a database within my InfluxDB instance via a POST request from an HTML form. I've done this successfully lots of times through curl
, but I can't make it work with a simple HTML form. Consider this HTML code:
<!doctype html>
<!-- this file is called like http://my.influx.server/my_page_name.html -->
<html>
<head>
<title>my simple html/influx sender</title>
<meta charset="UTF-8"/>
</head>
<body>
<form action="http://my.influx.server:8086/write?db=db_name" method="post" enctype="text/plain">
<input name="data" type="hidden" value="my_measurement,tag_name=stuff value=1"/>
<input type="submit" value="insert 1"/>
</form>
<form action="http://my.influx.server:8086/write?db=db_name" method="post" enctype="text/plain">
<input name="data" type="hidden" value="my_measurement,tag_name=stuff value=0"/>
<input type="submit" value="insert 0"/>
</form>
</body>
</html>
The curl
command for sending a 1
would be like:
curl -i -XPOST 'http://my.influx.server:8086/write?db=mydb' --data-binary 'my_measurement,tag_name=stuff value=1'
So I tried to make a simple HTML form with just 2 buttons. The code above is the closest I could get to at least try to process the "line interface" syntax, however I'm getting either an error message or just no response and I don't get anything in my InfluxDB. The error message from the code above is:
unable to parse 'data=my_measurement,tag_name=stuff value=1\r': invalid number
If you have a close look at the end of the string, you see a \r
that obviously gets added and I suspect that this breaks number parsing (I had something similar some time ago), but at least this seems to try to evaluate the line at all. However, I haven't found a way to remove or avoid the \r
. Has someone an idea how to achieve this?
Also, please consider the following additional information:
\r
should be syntactically correct.curl
command uses the --data-binary
parameter, but it seems I don't have anything like this in HTML. I'm aware of binary enctype
s like application/x-binary
, but they don't work, because they URL-encode the string and this won't pass the syntax check. The only enctype
I found that worked at least close enough is text/plain
.<input>
element has no name
attribute. Then I noticed that the curl
string was built like my_measurement,tag_name=stuff value=1
, possibly multiple such lines separated by \n
, which is not like POST key-value-pairs as in a=1&b=2
(i. e. there is no key, that would be the name
attribute). Trying to trick it with name="my_measurement,tag_name"
and value="stuff value=1"
(which would resemble the original string) was not successful and I still couldn't figure out, which key is expected. I tried with content
, query
etc. and ended up using data
. I kept this then because in the docs they talk about "data" and none of the keys made any difference, as long as one is provided. I suspect InfluxDB to just use the first POST variable ignoring the name, but I can't find any clear statement on this.<input>
types like just hidden
or a regular textbox hidden by style. This made no difference. Neither did visible elements.EDIT 1: I tried to reproduce the error with curl
:
curl -i -XPOST 'http://my.influx.server:8086/write?db=home' --data-binary 'my_measurement,tag_name=stuff value=1\r'
This led to the error message:
unable to parse 'my_measurement,tag_name=stuff value=1\\r': invalid number
with headers:
HTTP/1.1 400 Bad Request
Content-Type: application/json
Request-Id: ...
X-Influxdb-Build: OSS
X-Influxdb-Error: unable to parse 'my_measurement,tag_name=stuff value=1\r': invalid number
X-Influxdb-Version: 1.7.9
X-Request-Id: ...
Date: ...
Content-Length: 78
I conclude:
\r
seems to be differently encoded in the error message (characters \
and r
instead of an actual carriage return), but in the header it's only \r
, however it doesn't make a difference regarding the parsing error, so this is comparable.EDIT 2: I found out how to show the request headers from a call to curl
. The command is:
curl -v -XPOST 'http://my.influx.server:8086/write?db=db_name' --data-binary 'my_measurement,tag_name=stuff value=1'
The relevant portion of the output of the command is:
> POST /write?db=db_name HTTP/1.1
> Host: my.influx.server:8086
> User-Agent: curl/7.58.0
> Accept: */*
> Content-Length: 37
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 37 out of 37 bytes
< HTTP/1.1 204 No Content
< Content-Type: application/json
< Request-Id: ...
< X-Influxdb-Build: OSS
< X-Influxdb-Version: 1.7.9
< X-Request-Id: ...
< Date: Sat, 25 Jan 2020 10:54:11 GMT
I conclude:
curl
with --binary-data
is application/x-www-form-urlencoded
.Unfortunately I couldn't achieve to see the actual request body, so I'll try again with some URL-encoded variants. However, my_measurement,tag_name=stuff value=1
is 37 characters as in the request header, so I assume there is no key name like data
involved. Currently, I get the same error message I had before I posted this question: unable to parse 'data=my_measurement%2Ctag_name%3Dstuff+value%3D1': missing fields
The \r
is gone, but I still can't send data without a key name and the whole string is invalid due to URL-encoding. How to get rid of the URL-encoding?
Finally, I found a solution with JavaScript that worked. This Mozilla doc page was the key to a POST form without keys. My HTML page now looks like this:
<!doctype html>
<!-- this file is called like http://my.influx.server/my_page_name.html -->
<html>
<head>
<title>my simple html/influx sender</title>
<meta charset="UTF-8"/>
</head>
<body>
<form id="form1">
<button>insert 1</button>
</form>
<form id="form0">
<button>insert 0</button>
</form>
<script>
function sendData(value)
{
const str = "my_measurement,tag_name=stuff value=" + value;
const xhr = new XMLHttpRequest();
xhr.addEventListener("load", function(event) {
alert("Success");
});
xhr.addEventListener("error", function(event) {
alert("Error");
});
xhr.open("POST", "http://my.influx.server:8086/write?db=db_name");
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send(str);
}
const form0 = document.getElementById("form0");
const form1 = document.getElementById("form1");
form0.addEventListener("submit", function(event) {
event.preventDefault();
sendData(0);
});
form1.addEventListener("submit", function(event) {
event.preventDefault();
sendData(1);
});
</script>
</body>
</html>
Note the stripped-down form definitions: There are no action
s, method
s or enctype
s any more, as they are set via JavaScript. Also, there is no regular submit
element, instead it is a regular button, however I don't know if this is needed. I'll investigate that later.
The main part is in the script tag underneath the forms. A function sendData
prepares an XMLHttpRequest
object for POST
ing a prepared string and invokes its send
method. This function is used in the submit
events of each form. Also, this function registers event handlers for successful and failed requests.
The lines below the sendData
function identify the forms and register event listeners on their submit
event. Each listener prevents its form from submitting in a regular fashion and invokes the appropriate sendData
call instead, which will successfully insert values into InfluxDB.
Be aware, though, there is still no guarantee to detect every error. I tried to insert a string into an integer field, which failed, but I still got the "Success"-alert. I'm going to investigate that later.
So in total, I see this problem as sufficiently resolved for my purposes and I hope this helps anyone stumbling across it.