QtCUrl post doesn´t work anymore (Linux nok...windows ok) - c++

I have been running this function since last year (Linux and Windows) within my program.
Now I need to implement a new function and my new build is no longer running.
I have other CUrl functions using POST and the results are the same: nok, but my GET functions are ok.
I have another computer (with Mint 19) where this program is running smoothly, but on my computer (using Mint 19, too) the compilation is fine, but it starts curl.exec (I'm using Qtcurl library and inside has a call to curl_easy_perform) and no longer returns.
I have this package installed: libcurl4-openssl-dev
It's okay to compile my program (Linux and Windows). This program is running on Windows.
My problem is just new builds in Mint19.
What is missing to install?
QUrl url("https://pos-api.ifood.com.br/oauth/token");
QUrlQuery q;
q.addQueryItem("client_id", id);
q.addQueryItem("client_secret", secret);
q.addQueryItem("grant_type","password");
q.addQueryItem("username",user);
q.addQueryItem("password",password);
url.setQuery(q);
QtCUrl::Options opt;
opt[CURLOPT_URL] = url;
opt[CURLOPT_POST] = true;
opt[CURLOPT_FOLLOWLOCATION] = true;
opt[CURLOPT_FAILONERROR] = true;
opt[CURLOPT_SSL_VERIFYPEER]= false; // windows
QStringList headers;
headers
<< "cache-control: no-cache"
<< "content-type: application/x-www-form-urlencoded";
opt[CURLOPT_HTTPHEADER] = headers;
val = cUrl.exec(opt); // PROBLEM HERE!!!!
if (cUrl.lastError().isOk()) {
bool ok;
// json is a QString containing the JSON data
QtJson::JsonObject result = QtJson::parse(val, ok).toMap();
token=result["access_token"].toString();
return token;
}
else {
return "";
}

I changed all my methods.
The first funcion is a POST with query.
QString iFood_getToken2(QString token, int *expira, QString id, QString secret, QString user, QString password, QString host){
if(host!=hostname || !ifood_ativo){
qDebug() << "iFood_getToken2 saindo...";
return "";
}
if(*expira>IFOOD_TASK){
*expira-=IFOOD_TASK;
// qDebug() << "expira " << *expira;
return token; // token válido
}
QUrl url("https://pos-api.ifood.com.br/oauth/token");
QUrlQuery q;
q.addQueryItem("client_id", id);
q.addQueryItem("client_secret", secret);
q.addQueryItem("grant_type","password");
q.addQueryItem("username",user);
q.addQueryItem("password",password);
url.setQuery(q);
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QVariant(int(QNetworkRequest::AlwaysNetwork)));
QJsonObject json;
QNetworkAccessManager nam;
QNetworkReply *reply = nam.post(request, QJsonDocument(json).toJson());
while (!reply->isFinished())
{
qApp->processEvents();
}
QByteArray response_data = reply->readAll();
QJsonDocument jsonr = QJsonDocument::fromJson(response_data);
reply->deleteLater();
//qDebug() << "ifoodtoken2 " << jsonr["access_token"].toString();
return jsonr["access_token"].toString();
}
I did implement these new functions:
There is a new implementation for a GET and PATCH
So, from now on I dont need to use CUrl library anymore
QJsonDocument networkGet(QString strUrl, QString token){
QUrl url(strUrl);
QNetworkRequest request(url);
request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QVariant(int(QNetworkRequest::AlwaysNetwork)));
QString headerData = "bearer " + token;
request.setRawHeader("Authorization", headerData.toLocal8Bit());
QJsonObject json;
QNetworkAccessManager nam;
QNetworkReply *reply = nam.get(request);
while (!reply->isFinished())
{
qApp->processEvents();
}
QByteArray response_data = reply->readAll();
QJsonDocument json_response = QJsonDocument::fromJson(response_data);
reply->deleteLater();
//qDebug() << "networkGet " << json_response << reply->errorString() << headerData ;
return json_response;
}
int networkPatch(QString strUrl, QString token, QJsonDocument json){
QUrl url(strUrl);
QNetworkRequest request(url);
request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QVariant(int(QNetworkRequest::AlwaysNetwork)));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QString headerData = "bearer " + token;
request.setRawHeader("Authorization", headerData.toLocal8Bit());
QNetworkAccessManager nam;
QByteArray * _b_arr = new QByteArray (QString(json.toJson()).toLatin1());
QBuffer *_qbf_upload =new QBuffer (_b_arr);
QNetworkReply *reply = nam.sendCustomRequest(request,"PATCH",_qbf_upload);
while (!reply->isFinished())
{
qApp->processEvents();
}
QByteArray response_data = reply->readAll();
QJsonDocument json_response = QJsonDocument::fromJson(response_data);
reply->deleteLater();
qDebug() << "networkPatch " << reply->error() << json_response << reply->errorString() << headerData ;
return reply->error();
}

Related

Trouble when using QNetworkReply in QT

I'm new in C++ also with QT, my current QT version is 5.5.1.
I have an issue when using connect with QNetworkReply and QNetworkAccessManager. When I try to assign a variable from another file to data fetched from API, it always calls the function first before connecting to the API so that the variable is always empty. Can you guys help me?
void NioshFunc::fetchDataFromAPI(){
QUrl url;
QUrlQuery querystr;
querystr.addQueryItem("$format","json");
url.setScheme("https");
url.setHost("example.com");
url.setPath("/aaaa/dataset");
url.setQuery(querystr);
QNetworkRequest request;
request.setUrl(url);
request.setRawHeader("KeyId", "xxx-xxx-xxx");
reply = manager->get(request);
connect(reply, &QNetworkReply::readyRead, this, &NioshFunc::readyRead);
connect(reply, &QNetworkReply::finished, this, &NioshFunc::finished);
}
void NioshFunc::readyRead(){
dataBuffer.append(reply->readAll());
}
void NioshFunc::finished(){
if(reply->error())
{
qDebug() << "ERROR!";
qDebug() << reply->errorString();
qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
qDebug() << reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
}else{
qDebug() << "SUCCESS";
qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
qDebug() << reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
nioshWPDataFromAPI = QJsonDocument::fromJson(dataBuffer);
qDebug() << nioshWPDataFromAPI;
dataBuffer.clear();
}
reply->deleteLater();
}
bool NioshFunc::parse(){
Globals *g = Globals::getHandle();
if (nioshWPDataFromAPI.isEmpty() || nioshWPDataFromAPI.isNull()) return false;
QVariantMap root = nioshWPDataFromAPI.toVariant().toMap();
if (root.isEmpty()) return false;
QVariantMap d = root["d"].toMap();
if (d.isEmpty()) return false;
QVariantList results = d["results"].toList();
if (results.isEmpty()) return false;
foreach (QVariant varResult, results)
{
QVariantMap result = varResult.toMap();
if (result.isEmpty()) return false;
g->struct_tableColumn.input.duration = result["Duration"].toString();
g->struct_tableColumn.input.id = result["Id"].toString();
if (id.isEmpty() || id.isNull()) return false;
g->struct_tableColumn.input.load_cumul_Limit = result["LoadCumulLimit"].toString();
g->struct_tableColumn.input.Notes = result["Notes"].toString();
qDebug() << g->struct_tableColumn.input.id << g->struct_tableColumn.input.duration << g->struct_tableColumn.input.load_cumul_Limit << g->struct_tableColumn.input.Notes; // "it is empty for all variable from globals file.
}
return true;
}
When I can function fetchDataFromAPI() and call function parse() in another file named globals, that is always called 2 these functions first before connecting to API, so that the variable named "nioshWPDataFromAPI" is always empty. But when I read nioshWPDataFromAPI in function finished(), it still has data, but only in function finished(). Could you guys help me? Sorry if my question is not too clear for you guys

Read Json from https in qt

I wrote this code on qt, but when i run this project, My output is "Error".
How can solve my problem?
For example in this code I add a address in url, and I want read Json from this url, and show some info.
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QNetworkAccessManager* nam = new QNetworkAccessManager(this);
QString test = "ar";
QString test2 = "Hello World";
QObject::connect(nam, SIGNAL(finished(QNetworkReply*)),
this, SLOT(onResult(QNetworkReply*)));
QUrl url("https://translate.yandex.net/api/v1.5/tr.json/translate?key=trnsl.1.1.20180627T161429Z.7e64c91dd2016a6c.9901da9a44bc324388a2460776ab55b2d72b4c5a&lang=" + test + "&text=" + test2);
QNetworkReply* reply = nam->get(QNetworkRequest(url));
}
void MainWindow::onResult(QNetworkReply *reply)
{
if(reply->error() == QNetworkReply::NoError) {
QStringList propertyNames;
QStringList propertyKeys;
QString strReply = (QString)reply->readAll();
qDebug() << strReply;
QJsonDocument jsonResponse = QJsonDocument::fromJson(strReply.toUtf8());
QJsonObject jsonObject = jsonResponse.object();
QJsonArray jsonArray = jsonObject["status"].toArray();
qDebug() << jsonObject["status"].toString();
foreach (const QJsonValue & value, jsonArray)
{
QJsonObject obj = value.toObject();
qDebug() << value.toString();
}
} else {
qDebug() << "ERROR";
}
delete reply;
}
To add key-values ​​to the url you must use QUrlQuery as shown below:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
nam = new QNetworkAccessManager(this);
connect(nam, &QNetworkAccessManager::finished, this, &MainWindow::onResult);
QString lang = "ar";
QString text = "Hello World";
QString key = "trnsl.1.1.20180627T161429Z.7e64c91dd2016a6c.9901da9a44bc324388a2460776ab55b2d72b4c5a";
QUrlQuery query;
query.addQueryItem("key", key);
query.addQueryItem("lang", lang);
query.addQueryItem("text", text);
QUrl url("https://translate.yandex.net/api/v1.5/tr.json/translate");
url.setQuery(query);
qDebug()<< "url: "<< url.toString(QUrl::FullyEncoded);
nam->get(QNetworkRequest(url));
}
void MainWindow::onResult(QNetworkReply *reply){
if(reply->error() == QNetworkReply::NoError){
QByteArray result = reply->readAll();
QJsonDocument jsonResponse = QJsonDocument::fromJson(result);
QJsonObject obj = jsonResponse.object();
qDebug()<<"code: " << obj["code"].toInt();
qDebug()<<"lang: " << obj["lang"].toString();
QJsonArray array = obj["text"].toArray();
for(const QJsonValue & value : array) {
qDebug()<< "text: " <<value.toString();
}
}
else
qDebug() << "ERROR";
reply->deleteLater();
}
Output:
url: "https://translate.yandex.net/api/v1.5/tr.json/translate?key=trnsl.1.1.20180627T161429Z.7e64c91dd2016a6c.9901da9a44bc324388a2460776ab55b2d72b4c5a&lang=ar&text=Hello%20World"
code: 200
lang: "en-ar"
text: "مرحبا العالم"
If the url generated is revised, it differs from the concatenation:
Concatenation:
...&text=Hello World
Encoded:
...&text=Hello%20World

QJsonDocument to list or array in c++

I've tried this code and works, but I didn't understand how can get json and convert in array or list with Qt.
My code:
QEventLoop eventLoop;
QNetworkAccessManager mgr;
QObject::connect(&mgr, SIGNAL(finished(QNetworkReply*)), &eventLoop, SLOT(quit()));
QNetworkRequest req(QUrl(QString("http://myurljson.com/getjson")));
QNetworkReply *reply = mgr.get(req);
eventLoop.exec(); // blocks stack until "finished()" has been called
if (reply->error() == QNetworkReply::NoError) {
QString strReply = (QString)reply->readAll();
qDebug() << "Response:" << strReply;
QJsonDocument jsonResponse = QJsonDocument::fromJson(strReply.toUtf8());
QJsonObject jsonObj = jsonResponse.object();
qDebug() << "test:" << jsonObj["MCC_Dealer"].toString();
qDebug() << "test1:" << jsonObj["MCC_User"].toString();
delete reply;
}
else {
//failure
qDebug() << "Failure" <<reply->errorString();
delete reply;
}
my json get (3 records from url):
[{"MCC_Dealer":'test',"MCC_User":'test',"CurrentDealer":'test',"CurrentUser":'test'},{"MCC_Dealer":'test',"MCC_User":'test',"CurrentDealer":'test',"CurrentUser":'test'},{"MCC_Dealer":'test',"MCC_User":'test',"CurrentDealer":'test',"CurrentUser":'test'}]
I need to get json and set in list or in array.
My target is convert json response in array or list with c++ and Qt.
Any ideas?
Thanks
As I have mentioned in my comments, your JSON response is already an array, so you don't need to create additional structures to store the data you got. In order to de-serialize your data you can do the following:
[..]
QJsonArray jsonArray = jsonResponse.array();
for (auto it = jsonArray.constBegin(); it != jsonArray.constEnd(); ++it)
{
const QJsonValue &val = *it;
// We expect that array contains objects like:
// {"MCC_Dealer":'test',"MCC_User":'test',"CurrentDealer":'test',"CurrentUser":'test'}
QJsonObject o = val.toObject();
// Iterate over all sub-objects. They all have string values.
for (auto oIt = o.constBegin(); oIt != o.constEnd(); ++oIt)
{
// "MCC_Dealer":'test'
qDebug() << "Key:" << oIt.key() << ", Value:" << oIt.value().toString();
}
}

Can not read received data from QNetworkAccessManager::finished slot when received data is large [duplicate]

I want to see the results of a GET request. By my understanding, this code should do it. What am I doing wrong?
void getDoc::on_pushButton_2_clicked()
{
manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
manager->get(QNetworkRequest(QUrl("http://www.google.com")));
}
void getDoc::replyFinished(QNetworkReply *reply)
{
qDebug() << reply->error(); //prints 0. So it worked. Yay!
QByteArray data=reply->readAll();
qDebug() << data; // This is blank / empty
QString str(data);
qDebug() << "Contents of the reply: ";
qDebug() << str; //this is blank or does not print.
}
The code compiles and runs fine. It just doesn't work.
Try modifying your replyFinished slot to look like this:
QByteArray bytes = reply->readAll();
QString str = QString::fromUtf8(bytes.data(), bytes.size());
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
You can then print the statusCode to see if you are getting a 200 response:
qDebug() << QVariant(statusCode).toString();
If you are getting a 302 response, you are getting a status redirect. You will need to handle it like this:
if(statusCode == 302)
{
QUrl newUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
qDebug() << "redirected from " + replyUrl + " to " + newUrl.toString();
QNetworkRequest newRequest(newUrl);
manager->get(newRequest);
return;
}
I'm returning when encountering a status code of 302 since I don't want the rest of the method to execute.
I hope this helps!

Qt QNetworkReply is always empty

I want to see the results of a GET request. By my understanding, this code should do it. What am I doing wrong?
void getDoc::on_pushButton_2_clicked()
{
manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
manager->get(QNetworkRequest(QUrl("http://www.google.com")));
}
void getDoc::replyFinished(QNetworkReply *reply)
{
qDebug() << reply->error(); //prints 0. So it worked. Yay!
QByteArray data=reply->readAll();
qDebug() << data; // This is blank / empty
QString str(data);
qDebug() << "Contents of the reply: ";
qDebug() << str; //this is blank or does not print.
}
The code compiles and runs fine. It just doesn't work.
Try modifying your replyFinished slot to look like this:
QByteArray bytes = reply->readAll();
QString str = QString::fromUtf8(bytes.data(), bytes.size());
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
You can then print the statusCode to see if you are getting a 200 response:
qDebug() << QVariant(statusCode).toString();
If you are getting a 302 response, you are getting a status redirect. You will need to handle it like this:
if(statusCode == 302)
{
QUrl newUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
qDebug() << "redirected from " + replyUrl + " to " + newUrl.toString();
QNetworkRequest newRequest(newUrl);
manager->get(newRequest);
return;
}
I'm returning when encountering a status code of 302 since I don't want the rest of the method to execute.
I hope this helps!