How to send a REST call with Qt? - c++

I have started started working with Qt framework and after reading the documentation for Qt5 and some examples across some blogs, I wrote the following program but I does not seem to do the correct job.
I am writing a class for which I need to write a method Login and logout.
For login method, I am writing following code:
void User::login()
{
const QUrl loginUrl = (this->m_url).append("/api/auth/login");
QNetworkRequest loginRequest;
loginRequest.setUrl(loginUrl);
loginRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QJsonObject body;
QJsonObject data;
data.insert("userName", this->m_userName);
data.insert("password", this->m_password);
body.insert("data", data);
body.insert("provider", "LDAP");
//loginRequest.setBody(QJsonDocument(body).toJson());
const QByteArray json = QJsonDocument(body).toJson();
QNetworkReply* reply = m_manager.post(loginRequest, QJsonDocument(body).toJson());
while (!reply->isFinished())
{
// wait for the request to complete
}
QByteArray response_data = reply->readAll();
QJsonDocument responseJson = QJsonDocument::fromJson(response_data);
reply->deleteLater();
std::cout << response_data.toStdString() << std::endl;
}
After I call this method in my main function, If I check in fiddler, I cannot see any request made also, the program goes into infinite loop. Can you tell me what is wrong?

Cause
As you say, the program goes into infinite loop. The very one you have created:
while (!reply->isFinished())
{
// wait for the request to complete
}
Simply put, with QNetworkReply* reply = m_manager.post(loginRequest, QJsonDocument(body).toJson()); you do not send a request, but merely declare your wish to do so. Right after that you block the event loop, leaving Qt no chance to fulfill your wish.
Solution
Use signals and slots as intended. Think about team as events and callbacks. A good starting point is the description of QNetworkAccessManager:
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
connect(manager, &QNetworkAccessManager::finished,
this, &MyClass::replyFinished);

Related

Qt cpp cant send obj to connected function

I need to get a json file from an url, fill it in a QtNetworkReply *reply and send reply in a connected fonction to convert it in QbyteArray to pars my Json response.
But when i go in my connected function, i cant fill QByteArray with that reply (always empty)
Here's my code :
int main(int ac, char *av[])
{
Borne borne(ac, av);
reply myReply;
QNetworkAccessManager networkManager;
QUrl url("http://vps202498.ovh.net:8080/ws/rest/v.1/stores/categories/150/products");
QNetworkRequest request;
request.setUrl(url);
myReply._reply = networkManager.get(request);
QObject::connect(myReply._reply, SIGNAL(finished()), &myReply, SLOT(fonction()));
myReply._reply->finished();
exit(1);
if (borne.initialize() == false)
return (false);
return (borne._app->exec());
}
And here's my connected function :
IProduct *reply::fonction()
{
QByteArray List;
std::cout << "connected" << std::endl;
List = _reply->readAll();
if (List.isNull())
exit(6);
return (NULL);
}
My .H :
class reply : public QObject
{
Q_OBJECT
public:
reply() {};
~reply() {};
QNetworkReply *_reply;
public slots:
IProduct *fonction();
private :
};
I cant std::cout "connected", but always quit with error log '6'.
I dont really know where am i doing mistake (Iam used to C, not Cpp), i've read all the man of Qt about it, and cant figure what going wrong.
Any ideas?
Thank you and apologize for weak skill and english
You call the finish() function manually immediately after creation of the request. In that moment the request is not even started, so there is nothing to read from _reply->readAll(). The reply finished signal should be called by the even loop after calling application exec().
Remove lines:
myReply._reply->finished();
exit(1);
The request will be processed asyncronously in the event loop.
Other issues:
the slot reply::fonction() does not need any retrun value;
the event loop may be not started because of (borne.initialize() == false).

How to use QNetworkManager for REST api?

I would like to make a class for accessing data via REST API, for example:
class MeteoStation{
int getLatestTemperature();
int getLatestPessure();
private:
QNetworkManager nmng;
}
How could I implement this methods? Usually I was using something like:
int MeteoStation::getLatestTemperature(){
...
QEventLoop eventLoop;
connect(&m_nam,SIGNAL(finished(QNetworkReply*)),&eventLoop,SLOT(quit()));
QNetworkReply *reply = m_nam.get( req );
eventLoop.exec();
reply->readAll()
...
}
But since using inner QEventLoop is not recommended, how should I see to whom the response belong to?
MeteoStation::MeteoStation(){
connect(&nmam, SIGNAL(finished(QNetworkReply*)),
this, SLOT(parseNetworkResponse(QNetworkReply*)));
}
void MeteoStation::parseNetworkResponse( QNetworkReply *finished )
{
QByteArray data = finished->readAll();
...
Yes and it would be nice to have the class thread save. How are you solving that in your code?
How bad is making the call synchronous with:
QNetworkRequest req(url);
QScopedPointer<QNetworkReply> reply(nam.get(req));
QTime timeout= QTime::currentTime().addSecs(10);
while( QTime::currentTime() < timeout && !reply->isFinished()){
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
}
if (reply->error() != QNetworkReply::NoError) {
qDebug() << "Failure" <<reply->errorString();
}
QByteArray data = reply->readAll();
I've resolved my problem using QCoreApplication::processEvents(). The response is there within ms and I'm able to implement functionality close to libcurl.
QNetworkRequest req(url);
QScopedPointer<QNetworkReply> reply(nam.get(req));
QTime timeout= QTime::currentTime().addSecs(10);
while( QTime::currentTime() < timeout && !reply->isFinished()){
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
}
if (reply->error() != QNetworkReply::NoError) {
qDebug() << "Failure" <<reply->errorString();
}
QByteArray data = reply->readAll();
The Qt docs should provide all info you need.
You creat a nam, connect the finished signal, send the request.
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)),
this, SLOT(parseNetworkResponse(QNetworkReply*)));
manager->get(QNetworkRequest(QUrl("http://qt-project.org")));
Detecting to which request a reply belongs should not be too hard. The reply contains the url. It might be different, but not that different:
...but for a variety of reasons it can be different (for example, a
file path being made absolute or canonical).
QUrl QNetworkReply::url() const
Returns the URL of the content downloaded or uploaded. Note that the
URL may be different from that of the original request.

Simple version checker with Qt

I’m trying to implement a simple version checker below.
I’m getting a zero error code reading the file, but the file
contents show up blank. You can access the file via browser,
permissions are ok.
void check_version()
{
QNetworkAccessManager *nam = new QNetworkAccessManager();
QUrl data_url("http://www.example.com/version.txt");
QNetworkRequest req(data_url);
QNetworkReply *reply = nam->get(req);
QByteArray data = reply->readAll() ;
QString s1(data);
int err = reply->error();
QString s2 = QString::number(err);
delete reply;
delete nam;
QMessageBox::critical(0, "",s1+" "+s2,QMessageBox::Cancel);
}
I gather the problem is that I need to wait to read until the get is finished, so I need a signal and a slot: the signal tells the slot to read the data.
pseudocode:
QObject::connect(&rep, SIGNAL( rep is finished ),
QByteArray newver , SLOT( reply->readAll() ));
How do I set up a signal/slot for my task?
You're right, you have to wait until the get() is "finished" in order to obtain the whole response with a call to readAll().
Following a working example to start with:
// ...
QNetworkAccessManager *nam = new QNetworkAccessManager();
QUrl data_url("http://www.example.com/version.txt");
QNetworkReply* reply = nam->get(QNetworkRequest(data_url));
QEventLoop eventLoop;
QObject::connect(reply, SIGNAL(finished()), &eventLoop, SLOT(quit()));
eventLoop.exec();
if (reply->error() != QNetworkReply::NoError)
{
// Something went wrong. Error can be retrieved with: reply->error()
}
else
{
// Call reply->readAll() and do what you need with the data
}
// ...
This example will block until the reply is ready. If you need an asynchronous behavior you can just connect the signal finished() to a custom slot and check for error and/or read there. I do not recommend to connect the signal finished() directly to readAll() because "sometimes errors happen".

QNetworkAccessManager threads never finish

I know that in version 4.8 each http request gets its own thread to run.
I'm doing a links checker app that does a lot of http requests in a while loop and I notice in the windows task manager that my app is using more than 1600 threads over time and the number never goes down, only up until it crashes the app. (I'm guessing that is the cause.)
My question is, does QNetworkAccessManager have an option to use thread pool?
Or does it have an option to clean its threads after it finishes its http request?
This is the main loop:
while(!rpm_urlStack->isEmpty())
{
QString url = rpm_urlStack->top();
//define the reply
QNetworkReply *reply;
rpm_urlStack->pop();
QString urlForReq(url);
bool returnVal = true;
QNetworkRequest request;
request.setUrl(QUrl(urlForReq));
request.setRawHeader("User-Agent", USER_AGENT.toUtf8());
request.setRawHeader("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7");
request.setRawHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
request.setRawHeader("Accept-Language", "en-us,en;q=0.5");
request.setRawHeader("Connection", "Keep-Alive");
QEventLoop loop;
reply = m_networkManager->get(request);
connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exit();
if(!loop.isRunning()) {
loop.exec();
}
RequestFinishedHandler(reply);
// this is how I delete the reply object
delete reply;
}
RequestFinishedHandler(QNetworkReply *reply)
{
if (reply->error() > 0) {
QNetworkReply::NetworkError networkError = reply->error();
QString err = reply->errorString();
} else {
QVariant vStatusCodeV = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
QMutexLocker lock(_pMutex); // _pMutex defined as class member
char *buffer;
buffer = getCurrentDateTime();
QTextStream out(m_file);
out << buffer << " " << _sCurrentUrl << "\n";
lock.unlock();
if(vStatusCodeV.toInt() == 200) {
QString ApiResponse;
QByteArray data;
data=reply->readAll();
ApiResponse.append(QString::fromUtf8(data));
}
}
}
It appears that to be effective, the deleteLater method must be called from within an event loop, which must regain control of execution to handle garbage collection.
Maybe you should refactor your code to have the event loop replace your while loop. Alternatively, since you're not using the finished slot to process the reply, perhaps you can delete the reply directly at the end of the RequestFinishedHandler function.

QNetworkReply reply has no members in finishedSlot

Qt Creator is used as the ide for this small app that is being developed
I am attempting to use QNetworkAccessManager to retrieve some information from a website. After the request is 'posted' to the web, the finished() signal is triggered, however the pointer that is passed to the finishedSlot() function does not appear to be pointing to an instantiated object, it is just address of the ponter. The code for the button click that starts the request and the code for the finishedSlot() method is shown below.
In the watch window, I would have expected to see a triangle next to 'reply' that when expaned would show all the data member of QNetworkReply object. Instead it has a single value of #0x80c770 which looks like the pointer address.
I'd appreciate input from anyone who can help me to understand why my pointer doesn't appeart to be pointing the the QNetworkReply object.
void MainWindow::on_btnGetOAuthToken_clicked()
{
QUrl serviceUrl("https://api.ProPhotoWebsite.com/services/oauth/authorize.mg");
QUrl postData;
postData.addQueryItem("method", "ProPhotoWebsite.auth.getRequestToken");
postData.addQueryItem("oauth_consumer_key", "AAAAAAAAAAAAAAAAAAAAAAAA"); //example key
postData.addQueryItem("oauth_nonce",QUuid::createUuid().toString());
postData.addQueryItem("oauth_signature_method","PLAINTEXT");
postData.addQueryItem("oauth_signature","999999999999999999999999999"); //example
postData.addQueryItem("oauth_timestamp", QString::number(QDateTime::currentMSecsSinceEpoch()/1000));
postData.addQueryItem("oauth_version","1.0");
//...
QNetworkRequest request(serviceUrl);
request.setHeader(QNetworkRequest::ContentTypeHeader,
"application/x-www-form-urlencoded");
// Call the webservice
QNetworkAccessManager *nam = new QNetworkAccessManager(this);
connect(nam, SIGNAL(finished(QNetworkReply*)),
SLOT(finishedSlot(QNetworkReply*)));
nam->post(request,postData.encodedQuery());
}
void MainWindow::finishedSlot(QNetworkReply *reply)
{
// Reading attributes of the reply
// e.g. the HTTP status code
QVariant statusCodeV =
reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
// Or the target URL if it was a redirect:
QVariant redirectionTargetUrl =
reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
// see CS001432 on how to handle this
// no error received?
if (reply->error() == QNetworkReply::NoError)
{
QByteArray bytes = reply->readAll(); // bytes
QString string(bytes); // string
ui->lblWarning->setText(string);
}
else
{
// handle errors here
}
// need to dispose reply
delete reply;
}