POST-Request with JSON Data over HTTPS Connection with Qt5 - json

I'm trying to send a POST request over an HTTPS-Connection via Qt5 to a Web-API, but I keep getting the following error-message:
Failure "Error downloading https://.../login - server replied: BAD REQUEST"
Reply:"{"error_tag": "ARGUMENT_MISSING", "error_code": 19, "error_extra": {"argument": "email"}, "error": "Required argument is missing"}"
It seems like the HTTPS Connection works, but the POST request is faulty...
void connection::sendLoginData(){
QUrl url = QString("https://.../login");
QNetworkRequest req(url);
//Creating the JSON-Data
QJsonDocument json;
QJsonObject data;
data["email"] = QString("a#g.com");
data["password"] = QString("---");
json.setObject(data);
QByteArray jsonPost = QJsonDocument(data).toJson();
req.setHeader(QNetworkRequest::ContentTypeHeader,QVariant("application/json; charset=utf-8"));
req.setHeader(QNetworkRequest::ContentLengthHeader, QByteArray::number(jsonPost.size()));
//Sending the Request
QNetworkReply *reply = manager->post(req,jsonPost);
// Connection via HTTPS
QFile certFile(SSLCERTIFICATE);
certFile.open(QIODevice::ReadOnly);
QSslCertificate cert(&certFile, QSsl::Pem);
QSslSocket * sslSocket = new QSslSocket(this);
sslSocket->addCaCertificate(cert);
QSslConfiguration configuration = sslSocket->sslConfiguration();
configuration.setProtocol(QSsl::TlsV1_2);
sslSocket->setSslConfiguration(configuration);
reply->setSslConfiguration(configuration);
}
this is the Slot which is called when QNetworkReply gets a reply:
void connection::onFinished(QNetworkReply *reply){
if (reply->error() == QNetworkReply::NoError) {
//success
qDebug() << "Success" <<reply->readAll();
delete reply;
}
else {
//failure
qDebug() << "Failure" <<reply->errorString();
qDebug() << "Reply: " << reply->readAll();
delete reply;
}
}
The Signal "finished" of QNetworkReply is of course connected to the "onFinished"-Slot
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
manager = new QNetworkAccessManager;
ui->setupUi(this);
Connector = new connection(ui,manager);
connect(manager,SIGNAL(finished(QNetworkReply*)),Connector,SLOT(onFinished(QNetworkReply*)));
}
Maybe someone of you could tell me what's wrong with the POST-Request? Looking at the Reply of the Server it seems like the JSON-Data is never sent, or somehow formatted in a wrong way...

As you didn't provide the exact URL, I'd suggest you to try to check url.isValid() and url.errorString().
I just had an issue, the code worked fine in Qt 4.8, but in Qt 5.4 all my POST requests got 400 Bad Request reply.
I looked into the TCP dump via Wireshark and found out that the URL was wrong.
In my case url.setPath("api/register"); was the line that caused the problem. It should have been url.setPath("/api/register");
Hope it helps.

Related

ESP32 gives error on HTTP Post to Flask server

My goal is to post data to a Flask server. For this I have the following code running on a computer(Jupyter):
from flask import Flask
from flask import request
app = Flask(__name__)
#app.route('/postjson', methods = ['POST'])
def postJsonHandler():
print (request.is_json)
content = request.get_json()
print (content)
return 'JSON posted'
app.run(host='0.0.0.0', port= 8090)
On the esp I have the following function responsible for posting, Right now it is just for testing , I will further the functionality later on.
//Posts data to server
void post_to_server(String url)
{
HTTPClient http;
// Prepare JSON document
JsonObject root = doc.to<JsonObject>();
JsonArray pressure = root.createNestedArray("pressure");
JsonArray time = root.createNestedArray("time");
pressure.add("Pressure");
time.add("Time");
// Serialize JSON document
String json;
serializeJson(root, json);
// Send request
http.begin(url);
http.addHeader("Content-Type", "application/json");
int httpResponseCode = http.POST(json); //Send the actual POST request
// Read response
Serial.print(http.getString());
if (httpResponseCode > 0)
{
String response = http.getString(); //Get the response to the request
Serial.println(httpResponseCode); //Print return code
Serial.println(response); //Print request answer
}
else
{
Serial.print("Error on sending POST: ");
Serial.println(httpResponseCode);
// Disconnect
http.end();
}
}
So here is the odd thing, when I call the function on a test server like this:
post_to_server("http://jsonplaceholder.typicode.com/posts");
It works and I get the following response on the Serial Monitor as expected:
{
"pressure": [
"Pressure"
],
"time": [
"Time"
],
"id": 101
But when I try to post to the Server running on my PC like this:
post_to_server("http://127.0.0.1:8090/postjson");
I get the following error:
0
[E][WiFiClient.cpp:258] connect(): socket error on fd 54, errno: 104, "Connection reset by peer"
Error on sending POST: -1
I cant really make sense of this so I came here. I would appriciate any help. I also get the following when I test on Postman:
post_to_server("http://127.0.0.1:8090/postjson");
This will never work on your ESP32.
127.0.0.1 is the "loopback address" - the same as the name localhost. It's shorthand meaning "this computer".
When you use this with a program you run on your Windows machine, the program will attempt to connect to the Windows machine.
When you use this with your ESP32, it means connection to the ESP32.
You need to use the IP address associated with your Windows machine's network connection, whether ethernet or WiFi. 127.0.0.1 will not work.

fileAttachment.Load() not working for second attachment

I am getting
"The specified object was not found in the store., The process failed to
get the correct properties."
error while loading the FileAttachment.
Here is my code
foreach (Attachment attachment in message.Attachments)
{
if (attachment is FileAttachment)
{
if ( attachment.IsInline==true)
{
// in line image , may be part of signature image not considering for process
continue;
}
else
{
FileAttachment fileAttachment = attachment as FileAttachment;
string route1 = ConfigurationManager.AppSettings["route"];
string route = route1 + fileAttachment.Name;
String strFileUploadResut = "false";
try
{
fileAttachment.Load(route1 + fileAttachment.Name);
FilenetFactory FileNetUploadUtil = new FilenetFactory();
Console.WriteLine(" Sending File " + fileAttachment.Name + " to OneScan webservice.");
}
catch (Exception E)
{
log.logText("Exception during sending to webservice. "+ E.Message, "Info");
if (E.Message.ToString() == "The specified object was not found in the store., The process failed to get the correct properties.")
{ }
For First attachment its load properly. for second attachment i am getting this error. Not sure why its work first time and throws exception second time. What wrong i am doing here? any advice !!
Sounds like it might be another process working on the same message, eg if another process has moved the message between your calls to the server then Id's you have will be invalid and that would be the error you would expect to receive from the server. A way to test that would be to try to load the message after you receive that error.

How do I make a secure API request from an Arduino ESP32, programmed in the Arduino IDE using ArduinoJson?

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&currency=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("&currency='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&currency=\"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&currency=\"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. :)

POST request on esp8266 raw input/json with Arduino IDE

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());
}

C# Box-API [net_WebHeaderInvalidControlChars] received when downloading a file with Korean filename or when the file is corrupted

I received the following error when downloading from Box server:
InnerException {System.ArgumentException: [net_WebHeaderInvalidControlChars]
Arguments:
Debugging resource strings are unavailable. Often the key and arguments provide sufficient information to diagnose the problem. See http://go.microsoft.com/fwlink/?linkid=106663&Version=4.7.60408.0&File=System.Net.dll&Key=net_WebHeaderInvalidControlChars
Parameter name: name
at System.Net.Browser.ClientHttpWebRequest.InternalEndGetResponse(IAsyncResult asyncResult)
at System.Net.Browser.ClientHttpWebRequest.<>c__DisplayClasse.<EndGetResponse>b__d(Object sendState)
at System.Net.Browser.AsyncHelper.<>c__DisplayClass1.<BeginOnUI>b__0(Object sendState)} System.Exception {System.ArgumentException}
Shown below is the code snippet.
using (HttpClient client = new HttpClient(handler) { MaxResponseContentBufferSize = Int32.MaxValue })
{
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);
try
{
var fileResponse = await client.GetAsync(requestUrl, HttpCompletionOption.ResponseHeadersRead);
if (fileResponse != null && fileResponse.StatusCode == System.Net.HttpStatusCode.OK)
return await fileResponse.Content.ReadAsByteArrayAsync();
else return null;
}
catch(Exception e)
{
System.Diagnostics.Debug.WriteLine("Error in getAsync, " + e.StackTrace);
return null;
}
}
Note that this only happens for files with Korean (non-English) filenames and corrupted files. For image files and non-corrupted files, I was able to download successfully. (Example of corrupted file is a word or ppt file that shows an error msg when opened).
Having the same issue in windows phone 8.
I tried
HttpResponseMessage response = await httpClient.SendAsync(requestMessage);
I tried
WebClient client = new WebClient();
client.OpenReadAsync(new Uri(url,UriKind.RelativeOrAbsolute));
I tried IRestResponse from RestClient
All resulting in exception with empty value and inner exception net_WebHeaderInvalidControlChars.
The problem was that the user agent passed from windows client was not recognized server side. As a result, one from the returned response headers, was making .Net to crash in a lower level and the response could not be processed.
Adding NativeHost as the recognized UserAgent fixed the exception