I am building a project with ESP8266, SPIFFS and AsyncWebServerRequest.
The idea is to have a config.json file in the FS, that the ESP reads on start.
The config is big(around 2K) because it stores a lot of variables.
In addition I have index.html page, that I serve to the client via AsyncWebServerRequest
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(SPIFFS, "/index.html", "text/html");
});
When the page is loaded it makes second request to the ESP for the configuration and uses JS to propagate the configuration to the fields.
When you change the config from the web-page and click "Save Config" it encodes the form data in to JSON and make another request to the ESP(POST Request "Content-Type", "application/json" with the config. When the config is small it looks OK, but when the config is bigger it starts adding unidentified chars. I am not sure if I collect the http body properly:
server.on(
"/config/upload",
HTTP_POST,
[](AsyncWebServerRequest * request){},
NULL,
[](AsyncWebServerRequest * request, uint8_t *data, size_t len, size_t index, size_t total) {
for (size_t i = 0; i < len; i++) {
conf_f+=char(data[i]);
}
newConfig = 1;
request->send(200);
});
I think the issue is with the async, because it interrupts the concatenation process of the String. When I Serial.print the chars one by one it adds two of three new lines, that do not even have a char code.
Here is an example of the end of the config after failed upload: "enabled":"1","modbus_refresh_rate":"5","slaves_count":1���
This of course disrupt the ArduinoJSON and the system is not booting with the correct config.
My question is: Is there a better way to manipulate the HTTP body, because the one I am using looks wrong.
I have been hacking away at this for a few days with no luck.
I am trying to make a secure (SSL/HTTPS) API request in an Arduino environment. The controller I am using is an ESP32, which connects through wifi fine, and can retrieve/post data. However I am having no luck connecting to a secure API.
I'm trying to connect to this API https://strike.acinq.co/documentation/api-reference
EXAMPLE CURL REQUEST IN API'S DOCUMENTATION:
$ curl https://api.dev.strike.acinq.co/api/v1/charges \
-u sk_pJDwxFxCVw5fQJhRRMpf29jReUjjN: \
-X POST \
-d amount=42000 \
-d currency="btc" \
-d description="1%20Blockaccino"
Here is my Arduino code, I am using the ArduinoJson.h and WiFi.h libraries:
// Connect to HTTP server
WiFiClient client;
client.setTimeout(10000);
if (!client.connect("api.strike.acinq.co", 80)) {
Serial.println(F("Connection failed"));
return;
}
Serial.println(F("Connected!"));
// Send HTTP request
client.println(F("GET /api/v1/charges?id=MYKEY&amount=4200¤cy=btc HTTP/1.0"));
client.println(F("Host: api.strike.acinq.co"));
client.println(F("Content-Type: application/x-www-form-urlencoded"));
client.println(F("Connection: close"));
if (client.println() == 0) {
Serial.println(F("Failed to send request"));
return;
}
// Check HTTP status
char status[32] = {0};
client.readBytesUntil('\r', status, sizeof(status));
if (strcmp(status, "HTTP/1.1 200 OK") != 0) {
Serial.print(F("Unexpected response: "));
Serial.println(status);
return;
}
A 401 "Invalid API Key" Is the closest I have got. I know the API-key works, and that I am just using it wrong. I've tried moving the key to:
client.println(F("id: MYKEY"));
but that didn't work either.
I have tried other libraries and ArduinoJson seems to be the best. I think the issue is the fact its a secure server and the layout of my request. I found many resources for connecting to open API's on Arduino, but nothing on connecting to secure ones. I think I am almost there with the code...
UPDATE
So I have updated my code. I am still trying to use ArduinoJson. I can connect to the API but it keeps spitting out "HTTP/1.1 400 BAD_REQUEST". I don't know weather this is because its over HTTPS or the formatting of my request.
In the API docs -u and -X don't have a field name like "amount=4200", so I am assuming -u would just be added client.print("?="+apiKey);
//open weather map api key
String apiKey= "myapikey";
int status = WL_IDLE_STATUS;
char server[] = "api.strike.acinq.co";
Serial.println("\nStarting connection to server...");
// if you get a connection, report back via serial:
if (client.connect(server, 80)) {
Serial.println("connected to server");
// Make a HTTP request:
client.print("POST /api/v1/charges");
client.print("?="+apiKey);
client.print("&amount=4200");
client.print("¤cy='btc'");
client.println("&description='sweets'");
client.println("Host: api.strike.acinq.co");
client.println("Connection: close");
client.println();
}
else {
Serial.println("unable to connect");
}
UPDATE
I figured out the println and print actually mean something and have subsequently organised my request much better. It still comes back with 400 Unauthorized?
String PostData = "&description=\"car\"&amount=1000¤cy=\"sweetsandthat\"";
client.println("POST /api/v1/charges HTTP/1.1");
client.println("Host: api.strike.acinq.co");
client.println("Authorization: Basic "+apiKey);
client.print("Content-Length: ");
client.println(PostData.length());
client.println(); // blank line required
client.println(PostData);
Serial.println("POSTED DATA: " + PostData);
// client.stop();
client.println();
} else {
Serial.println("unable to connect");
}
delay(1000);
String line = "";
while (client.connected()) {
line = client.readStringUntil('999');
Serial.println(line);
Serial.println("parsingValues");
//create a json buffer where to store the json data
StaticJsonBuffer<5000> jsonBuffer;
JsonObject& root = jsonBuffer.parseObject(line);
if (!root.success()) {
Serial.println("parseObject() failed");
return;
}
//get the data from the json tree
String nextWeatherTime0 = root["id"][0];
// Print values.
Serial.println(nextWeatherTime0);
}
client.println("Connection: close");
client.stop();
}
Check the response for a BAD request, We usually get it when we deal with a bad URL or URL not found. check whether you are connecting to the same url mentioned in docs.
First connect to the api and after that make queries like providing your api key and feilds
remove this.
client.println("Host: api.strike.acinq.co");
and use GET request to get the response of the data you have in these fields
String PostData = "&description=\"car\"&amount=1000¤cy=\"sweetsandthat\""
I have also been struggling to get an https post to work on the esp32. A few things, the wifi.h module, I believe, does not support https. The WiFiClientSecure.h does, and you need to set the port to 443. I have also failed to get a POST to work, but I succeed in a basic GET test connection to howsmysssl.com. Andreas Spiess covers this well in a youtube video. He goes beyond SSL to establishing trust. I just want basic SSL to work, so if you get this figured out, please let me know. Hopefully I got you one step closer. :)
I'm developing a web service to serve json objects to a jeasyui async tree. My HTML has the following:
<ul id="tt" method="POST" class="easyui-tree" url="http://w.x.y.z:1024/testrest">
</ul>
Assume w.x.y.z is my server's IP address. According to the jeasyui documentation for their PHP json service, I need to return an array of dictionary objects that have keys id, text, and state. Okay, so far so good. I am attempting to develop a json service in c++ using the cpprest-sdk from Microsoft. I compiled and installed this library on RHEL 7.2 and am able to write some basic services using it. The problem lies (I think) with the encoding of the json that gets sent back to the client.
Here's a fully-functional example json server written with cpprest-sdk that handles POST requests and replies with a singly-populated array of dictionary objects that conform to the protocol expected by jeasyui:
#include <cpprest/http_listener.h>
#include <cpprest/json.h>
#pragma comment(lib, "cpprestlib" )
using namespace web;
using namespace web::http;
using namespace web::http::experimental::listener;
#include <iostream>
#include <map>
#include <set>
#include <string>
using namespace std;
#define TRACE(msg) wcout << msg
void handle_request(http_request request, function<void(const json::value &, json::value &, bool)> action)
{
json::value answer;
TRACE("\nHandle_request\n");
// Spit out the HTTP header to the console...
const auto HeaderString = request.to_string();
wcout << HeaderString.c_str() << endl;
request
.extract_json()
.then([&answer, &action](pplx::task<json::value> task) {
try
{
const auto & jvalue = task.get();
if (!jvalue.is_null())
{
action(jvalue, answer, false);
}
else
{
action(jvalue, answer, true);
}
}
catch (http_exception const & e)
{
wcout << "HTTP exception in handle_request: " << e.what() << endl;
}
})
.wait();
request.reply(status_codes::OK, answer);
}
void handle_post(http_request request)
{
TRACE("\nHandle POST\n");
handle_request(
request,
[](const json::value & jvalue, json::value & answer, bool bNull)
{
const utility::string_t sID("id");
const utility::string_t sText("text");
const utility::string_t sState("state");
if( bNull )
{
wcout << "jvalue must be null, setting some default values..." << endl;
json::value group;
group[sID] = json::value::string("1");
group[sText] = json::value::string("Hello");
group[sState] = json::value::string("closed");
answer[0] = group;
}
else
{
// To be written once the null case is sorted
}
}
);
}
int main()
{
uri_builder uri("http://w.x.y.z:1024/testrest");
http_listener listener(uri.to_uri());
listener.support(methods::POST, handle_post);
try
{
listener
.open()
.then([&listener]()
{
TRACE(L"\nStarting to listen\n");
})
.wait();
while (true);
}
catch (exception const & e)
{
wcout << e.what() << endl;
}
return 0;
}
This compiles cleanly and I can start the service on the linux server with the following:
./testrest &
Starting to listen
To aid in debugging, I've been using curl to serve as a POST client directly on the same linux server. I've been using the following command to send a POST request with 0 content-length:
curl -i -X POST -H 'Content-Type: application/json' http://w.x.y.z:1024/testrest
The output from curl is the following:
HTTP/1.1 200 OK
Content-Length: 44
Content-Type: application/json
[{"id":"1","state":"closed","text":"Hello"}]
and the console messages from my service are as such:
Handle POST
Handle_request
POST /testrest HTTP/1.1
Accept: */*
Content-Type: application/json
Host: w.x.y.z:1024
User-Agent: curl/7.29.0
jvalue must be null, setting some default values...
The first two lines correspond to the TRACE calls in the code. The middle section is generated by this section of code:
// Spit out the HTTP header to the console...
const auto HeaderString = request.to_string();
wcout << HeaderString.c_str() << endl;
Based on the curl output, which is an array of dictionary objects exactly one entry long, I would expect that this service should work just fine with the jeasyui javascript on the client. However, it does not. My async tree never populates and I don't see anything at all.
I suspect there's something wrong with the encoding, and so I wrote another service using web2py to test to see if it would work there. The following code exists in my default.py controller:
#service.json
def testweb2py():
aRet=[]
if request.post_vars.id is None:
mydict={'id':'1','text':'Hello','state':'closed'}
aRet.append(mydict)
return aRet
after modifying my client easyui-tree HTML to point to the web2py URL, it populates perfectly and I can see the node. I hit the web2py service.json code with curl just to see how the output might differ:
HTTP/1.1 200 OK
Date: Mon, 23 Jan 2017 18:17:17 GMT
Server: Apache/2.4.6 (Red Hat Enterprise Linux) OpenSSL/1.0.1e-fips mod_wsgi/3.4 Python/2.7.5
X-Powered-By: web2py
Expires: Mon, 23 Jan 2017 18:17:18 GMT
Pragma: no-cache
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Content-Length: 99
Content-Type: application/json; charset=utf-8
[{"text": "Hello", "state": "closed", "id": "1"}]
Aside from the content header being quite different, there's one line that I suspect might have something to do with it:
Content-Type: application/json; charset=utf-8
In the call to the cpprest service, the header output from curl does not include charset=utf-8 in it. If I dump the curl output to a file using the -o switch, I don't see any clear difference between the encoding. The only thing that I can see different in the format of the json is some extra whitespace and the ordering:
[{"text": "Hello", "state": "closed", "id": "1"}] // web2py version
[{"id":"1","state":"closed","text":"Hello"}] // cpprest version
I'm unable to gain any control over the order in which the json dictionary is sent, but I doubt that has anything to do with it anyways. The extra whitespace prefixing the value entry seems irrelevant as well.
I've poured over the cpprest documentation over at microsoft.github.io/cpprestsdk/index.html,
and I cannot find anything that relates to setting the output encoding. There are a number of overrides to http_request::reply that include options for setting content-type, and I've gone down the road of calling them with hard-coded strings for both the json body and the content-type of json/application; charset=utf-8, all to no avail. I don't see how those overrides can be used with json::value objects at any rate, so I don't think that's the optimal path or a viable use of this cpprest library.
The jeasyui javascript code appears to be intentionally obfuscated, and I have little faith in being able to figure out what it is doing with the reply from the POST call. Maybe someone familiar with jeasyui can point to a viable means for debugging the async POST?
Please help!
So I figured out what was happening. Opened the developer tools console in Chrome and discovered the following error message:
XMLHttpRequest cannot load http://w.x.y.z:1024/testrest. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://w.x.y.z' is therefore not allowed access.
So it had nothing to do with the format or encoding of my json data, but rather the fact that the json service was identified as being a different resource from the web server that generated the client HTML, which it is, and so Chrome was blocking it. To solve this problem, I had to add some header fields in the response I sent back to the client, as well as add a support method to handle OPTIONS queries from any client that might need them.
In my main() function, I added:
listener.support(methods::OPTIONS, handle_options);
Then I wrote the corresponding function:
void handle_options(http_request request)
{
http_response response(status_codes::OK);
response.headers().add(U("Allow"), U("POST, OPTIONS"));
// Modify "Access-Control-Allow-Origin" header below to suit your security needs. * indicates allow all clients
response.headers().add(U("Access-Control-Allow-Origin"), U("*"));
response.headers().add(U("Access-Control-Allow-Methods"), U("POST, OPTIONS"));
response.headers().add(U("Access-Control-Allow-Headers"), U("Content-Type"));
request.reply(response);
}
Finally, I had to add the same headers to the request.reply in my handle_request:
http_response response(status_codes::OK);
// Without these headers, the client browser will likely refuse the data and eat it
response.headers().add(U("Access-Control-Allow-Origin"), U("*"));
response.headers().add(U("Access-Control-Allow-Methods"), U("POST, OPTIONS"));
response.headers().add(U("Access-Control-Allow-Headers"), U("Content-Type"));
response.set_body(answer);
request.reply(response);
There were additional problems, as well... the most prominent being the fact that the jeasyui class easyui_tree does not POST data with a Content-Type of application/json. Instead, it posts a Content-Type of application/x-www-form-urlencoded, so I had to add a function to parse the url encoding using libcurl. This also meant replacing request.extract_json() with request.extract_string() and related modifications to the corresponding lambda functions used by cpprest.
Here's the final example code, maybe it's useful to others working in these areas. It's a fully-functional example of writing a json service with cpprest (on linux, no less) that responds to asynchronous POST requests from easyui_tree. Dependencies: boost, cpprest, and libcurl-devel.
#include <boost/algorithm/string/replace.hpp>
#include <cpprest/http_listener.h>
#include <cpprest/json.h>
#include <curl/curl.h>
#pragma comment(lib, "cpprestlib" )
using namespace web;
using namespace web::http;
using namespace web::http::experimental::listener;
#include <iostream>
#include <map>
#include <vector>
#include <set>
#include <string>
using namespace std;
#define TRACE(msg) wcout << msg
void build_json( const utility::string_t &source, json::value &jvalue )
{
// Use libcurl to unescape the POST body for us
vector<string> splitvec;
// We don't own the string created by curl_easy_unescape, so add a custom deleter
string text = shared_ptr<char>( curl_easy_unescape( 0, source.c_str(), 0, 0 ), curl_free).get();
// This works for this specific example of jeasyui, the class 'easyui-tree', which only passes id=... in the POST.
// Need custom handler to deal with more complicated data formats
boost::split( splitvec, text, boost::is_any_of("="));
if( splitvec.size() == 2 )
{
jvalue[splitvec.at(0)] = json::value::string(splitvec.at(1));
}
}
void handle_request(http_request request, function<void(const json::value &, json::value &, bool)> action)
{
json::value answer;
auto objHeader = request.headers();
auto sContentType = objHeader["Content-Type"];
// Two cases:
// 1) The very first call from easyui_tree, when the HTML is first loaded, will make a zero-length POST with no 'Content-Type' in the header
// 2) Subsequent calls from easyui_tree (e.g. when user opens a node) will have a Content-Type of 'application/x-www-form-urlencoded'
// Nowhere does easyui_tree send json data in the POST, although it expects json in the reply
if( sContentType.size() == 0 ||
!strncasecmp( sContentType.c_str(), "application/x-www-form-urlencoded", strlen("application/x-www-form-urlencoded") ) )
{
request
.extract_string()
.then([&answer, &action](pplx::task<utility::string_t> task) {
try
{
const auto & svalue = task.get();
json::value jvalue;
if ( svalue.size() == 0 )
{
action(jvalue, answer, true);
}
else
{
build_json( svalue, jvalue );
action(jvalue, answer, false);
}
}
catch (http_exception const & e)
{
wcout << "HTTP exception in handle_request: " << e.what() << endl;
}
})
.wait();
}
else
{
// This Content-Type doesn't appear with easyui_tree, but perhaps it's still useful for future cases...
if( !strncasecmp( sContentType.c_str(), "application/json", strlen("application/json") ) )
{
request
.extract_json()
.then([&answer, &action](pplx::task<json::value> task) {
try
{
const auto & jvalue = task.get();
if (!jvalue.is_null())
{
action(jvalue, answer, false);
}
else
{
action(jvalue, answer, true);
}
}
catch (http_exception const & e)
{
wcout << "HTTP exception in handle_request: " << e.what() << endl;
}
})
.wait();
}
}
http_response response(status_codes::OK);
// Without these headers, the client browser will likely refuse the data and eat it
response.headers().add(U("Access-Control-Allow-Origin"), U("*"));
response.headers().add(U("Access-Control-Allow-Methods"), U("POST, OPTIONS"));
response.headers().add(U("Access-Control-Allow-Headers"), U("Content-Type"));
response.set_body(answer);
request.reply(response);
}
void handle_options(http_request request)
{
http_response response(status_codes::OK);
response.headers().add(U("Allow"), U("POST, OPTIONS"));
// Modify "Access-Control-Allow-Origin" header below to suit your security needs. * indicates allow all clients
response.headers().add(U("Access-Control-Allow-Origin"), U("*"));
response.headers().add(U("Access-Control-Allow-Methods"), U("POST, OPTIONS"));
response.headers().add(U("Access-Control-Allow-Headers"), U("Content-Type"));
request.reply(response);
}
void handle_post(http_request request)
{
handle_request(
request,
[](const json::value & jvalue, json::value & answer, bool bInitialize)
{
if( bInitialize )
{
// First time the tree is being loaded, first id will be 16, which will yield us 16 child nodes when it POSTs back
json::value jreply;
jreply[U("id")] = json::value::string("16");
jreply[U("text")] = json::value::string("Parent");
jreply[U("state")] = json::value::string("closed");
answer[0] = jreply;
}
else
{
// User has opened a node
if( jvalue.type() == json::value::value_type::Object )
{
if( jvalue.has_field( "id" ) )
{
auto & key = jvalue.at( "id" );
if( key.is_string() )
{
auto value = key.as_string();
int id = atoi(value.c_str());
stringstream ss;
ss << (id / 2); // Each successive layer has half as many child nodes as the one prior
for( int i = 0; i < id; i++ )
{
json::value jreply;
jreply[U("id")] = json::value::string(ss.str());
jreply[U("text")] = json::value::string("Child");
jreply[U("state")] = json::value::string("closed");
answer[i] = jreply;
}
}
}
}
}
}
);
}
int main()
{
uri_builder uri("http://yourserver.com:1024/testrest");
http_listener listener(uri.to_uri());
listener.support(methods::POST, handle_post);
listener.support(methods::OPTIONS, handle_options);
try
{
listener
.open()
.then([&listener]()
{
TRACE(L"\nStarting to listen\n");
})
.wait();
while (true);
}
catch (exception const & e)
{
wcout << e.what() << endl;
}
return 0;
}
And then of course the corresponding HTML, assuming all the jeasyui scripts are referenced in the header:
<ul id="tt" method="POST" class="easyui-tree" url="http://yourserver.com:1024/testrest">
</ul>
Trying to learn Web Services with Qt (using Qt Creator 4.1.0) and connecting data to the GUI. I have read several online examples (most notably: 1, 2 and 3) but my low coding level along with the fact that I could not find full examples that were demonstrating my needs lead me here :).
I have created a simple example so that contains all my shortcomings:
make an HTTP get request to a (existing) web service every 30 seconds.
The web service then responds by sending a json data object (see below for such a json example format) which we receive and parse.
Then, the Qt will display all the parsed json data onto a simple GUI (see below for how such a GUI looks like.
json data format - example:
{
"city": "London",
"time": "16:42",
"unit_data":
[
{
"unit_data_id": "ABC123",
"unit_data_number": "21"
}
]
}
My simple Qt GUI design (made in Qt Creator) displaying all the fetched parsed data:
I would really appreciate any full code example that show how we can make a request to a web service and then how to fetch the json response. Finally, how to connect the GUI in Qt to display this data as soon as they are received.
I am just starting studying this area and I am in need of a simple full code example to get me going.
Here is a fully working example on how to send a GET request with parameters to a web service using QNetworkAccessManager and parse the JSON response using QJsonDocument.
In the example, I am sending a request to http://uinames.com/ whose responses are encoded in JSON in the following format:
{
"name":"John",
"surname":"Doe",
"gender":"male",
"region":"United States"
}
I am parsing the JSON response and displaying it in a GUI.
#include <QtWidgets>
#include <QtNetwork>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//setup GUI (you could be doing this in the designer)
QWidget widget;
QFormLayout layout(&widget);
QLineEdit lineEditName;
QLineEdit lineEditGender;
QLineEdit lineEditRegion;
auto edits = {&lineEditName, &lineEditGender, &lineEditRegion};
for(auto edit : edits) edit->setReadOnly(true);
layout.addRow("Name:", &lineEditName);
layout.addRow("Gender:", &lineEditGender);
layout.addRow("Region:", &lineEditRegion);
QPushButton button("Get Name");
layout.addRow(&button);
//send request to uinames API
QNetworkAccessManager networkManager;
QObject::connect(&networkManager, &QNetworkAccessManager::finished,
[&](QNetworkReply* reply){
//this lambda is called when the reply is received
//it can be a slot in your GUI window class
//check for errors
if(reply->error() != QNetworkReply::NoError){
for(auto edit : edits) edit->setText("Error");
networkManager.clearAccessCache();
} else {
//parse the reply JSON and display result in the UI
QJsonObject jsonObject= QJsonDocument::fromJson(reply->readAll()).object();
QString fullName= jsonObject["name"].toString();
fullName.append(" ");
fullName.append(jsonObject["surname"].toString());
lineEditName.setText(fullName);
lineEditGender.setText(jsonObject["gender"].toString());
lineEditRegion.setText(jsonObject["region"].toString());
}
button.setEnabled(true);
reply->deleteLater();
});
//url parameters
QUrlQuery query;
query.addQueryItem("amount", "1");
query.addQueryItem("region", "United States");
QUrl url("http://uinames.com/api/");
url.setQuery(query);
QNetworkRequest networkRequest(url);
//send GET request when the button is clicked
QObject::connect(&button, &QPushButton::clicked, [&](){
networkManager.get(networkRequest);
button.setEnabled(false);
for(auto edit : edits) edit->setText("Loading. . .");
});
widget.show();
return a.exec();
}
Edit:
Since you asked about how to use a QTimer to trigger the update every one minute, replace the connect call of the button clicked signal from the code above with something like this:
QTimer timer;
QObject::connect(&timer, &QTimer::timeout, [&](){
networkManager.get(networkRequest);
button.setEnabled(false);
for(auto edit : edits) edit->setText("Loading. . .");
});
timer.start(60000); //60000 msecs = 60 secs
As noted in comments, if you are using this in your window class's constructor, you have to make sure that the networkManager, networkRequest, the GUI components, and the timer here are kept alive as long as your window object is running. So, you may choose to allocate them in the heap or as class members.
I am trying to send post request with esp8266 programmed on arduino IDE, but there's lack of examples how to do it. I would like to send request to json server with raw input so it would look like this:
http://ip:port/something
BODY
{
"valuename":value
}
Would be grateful if anyone could show me such an example.
Greetings
For handling HTTP requests, you can use a RestClient library rather than writing all the low level requests. It saves a lot of time and is less error-prone.
For example, for a GET request, all you have to do is:
String response = "";
int statusCode = client.post("/", "foo=bar", &response);
One good such library with SSL support is written by github user DaKaz.
You can use it for your GET request. The returned response will be without the HTTP header. The function will return the response from the server without the headers.
Now you can use the ArduinoJson Library by bblanchin for decoding the JSON object.
Details can be seen here.
Or you can do plain string manipuation to get the values though it is not a recommended route to take and is prone to errors.
Here is an example to send JSON via HTTP library :
#include <ESP8266WiFi.h>
#include <ArduinoJson.h>
#include <ArduinoHttpClient.h>
#define JSON_BUF_SIZE 256
WiFiClient wifi;
HttpClient poster = HttpClient(wifi, IP, PORT);
void HTTPPost(){
String contentType = "application/json";
StaticJsonBuffer<JSON_BUF_SIZE> jsonBuffer;
JsonObject& jsonData = jsonBuffer.createObject();
jsonData["valuename"] = "value";
String postData = "";
jsonData.printTo(postData);
poster.post("/", contentType, postData);
printf("Trace : ResponseCode : %d\n", poster.responseStatusCode());
printf("Trace : Incoming Body : %s\n", poster.responseBody().c_str());
}