QNetworkAccessManager issue - c++

QString My_class::My_Method()
{
QNetworkAccessManager *manager= new QNetworkAccessManager(this);
connect(manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(ReplayFinished(QNetworkReply*)));
manager->get(QNetworkRequest(QUrl(My_URL)));
return str;
}
void My_class::ReplayFinished(QNetworkReply *replay)
{
QString buffer;
if(replay->isOpen())
{
buffer=replay->readAll();
//treatment on the buffer and the public Qstring 'str'(declared in My_class.h) is updated
}
}
Hi,
the problem is that when calling the My_method() in the main, the QString str is empty because it was returned that way without waiting for the ReplayFinished to update it.
What could be the solution to wait until the job is complete so I can get the proper information, not skipping it and returning something else.
Thank you.

You can use an event loop to wait until the reply is finished and then read the available bytes and return the string :
QString My_class::My_Method()
{
QNetworkAccessManager manager;
QNetworkReply *reply = manager.get(QNetworkRequest(QUrl(My_URL)));
QEventLoop loop;
connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), &loop, SLOT(quit()));
loop.exec();
QByteArray bts = reply->readAll();
QString str(bts);
delete reply;
return str;
}

Related

QNetworkReply - Strange error in enum, OperationCanceled instead of Timeout?

My API server is turned off and i run following code.
I dont understand why QNetworkReply::OperationCanceledError error enum is returned instead of QNetworkReply::TimeoutError. What is wrong? Am i doing something wrong or is it Qt bug?
From documentation that error should be if "the operation was canceled via calls to abort() or close() before it was finished."
I see no reason for that.
QByteArray encodedData = data.toUtf8();
QUrl url("http://myapi/jsonrpc");
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QNetworkAccessManager manager;
manager.setTransferTimeout(500);
QNetworkReply* reply = manager.post(request, encodedData);
QEventLoop loop;
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
loop.exec();
if (reply->error() != QNetworkReply::NoError) {
QString errorMsg = QString("HTTP Network request has failed. Code: ") + QVariant::fromValue(reply->error()).toString();
delete reply;
// error
// here i got QNetworkReply::OperationCanceledError
}
QByteArray response = reply->readAll();
//ok

QNetworkReply::NetworkError(ProtocolUnknownError)

In my project, I am attempting to download a YouTube profile picture. The YouTube API returns this link https://yt3.ggpht.com/-7ipuUvDjVT8/AAAAAAAAAAI/AAAAAAAAAAA/hSPOcUsb1nw/s240-c-k-no-mo-rj-c0xffffff/photo.jpg
However, using the same function for downloading files from my own website doesn't work with this URL. I get QNetworkReply::NetworkError(ProtocolUnknownError) in the application output.
Here is my code:
QByteArray downloadFileData(QString url)
{
QNetworkRequest request;
request.setUrl(QUrl(url));
QNetworkReply *reply = manager->get(request);
QEventLoop loop;
connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
if(reply->error() == QNetworkReply::NoError)
{
return reply->readAll();
}
else
{
qDebug() << reply->error();
}
reply->deleteLater();
return "";
}
When I pass that url to this function, I get that error. What is going on?
Thanks for your time

What's the best way to delete a QNetworkReply?

I'm writing an embedded RESTful API client using the Qt5 Embedded layer of OpenEmbedded project. I want my client to be able to send a simple async request to notify my server on what's going on and an other scenario is that my client needs to synchronize data from the server.
To do so I wrote two functions, one to send the request and another to get the response. So if I don't care about the response, I only use the first one.
To be able to use the response with the second one, I use a QNetworkReply * as a member of my class. To keep the QNetworkReply alive, I also set the QNetworkAccessManager as a member of my class.
#include <QtNetwork>
class ApiClient : public QObject
{
Q_OBJECT
public:
ApiClient(QObject *parent = 0);
private:
QString host;
QString key;
quint32 replyTimeout;
QNetworkAccessManager manager;
QNetworkReply *reply;
void sendRequest(const QString &method, const QVariantMap &params = QVariantMap());
QVariantMap getResponse() const;
};
The apiclient.cpp file:
#include "apiclient.h"
ApiClient::ApiClient(QObject *parent) : QObject(parent)
{
}
void ApiClient::sendRequest(const QString &method, const QVariantMap &params)
{
QUrl url(QString("%1/%2/").arg(host).arg(method));
QUrlQuery query;
query.addQueryItem("key", key);
if (!params.empty())
{
QMapIterator<QString, QVariant> it(params);
while (it.hasNext())
{
it.next();
query.addQueryItem(it.key(), it.value().toString());
}
}
url.setQuery(query);
qDebug() << "url: " << url.toString();
reply = manager.get(QNetworkRequest(url));
}
QVariantMap ApiClient::getResponse() const
{
if (!reply)
{
qFatal("No request sent!");
}
QTimer timer;
timer.setSingleShot(true);
QEventLoop loop;
connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
timer.start(replyTimeout);
loop.exec();
if (timer.isActive())
{
timer.stop();
if (reply->error())
{
qFatal("Wrong reply!");
}
int code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (code != 200)
{
qFatal("Invalid server response!");
}
QJsonDocument result = QJsonDocument::fromJson(reply->readAll());
if (result.isNull())
{
qFatal("Invalid JSON!");
}
reply->deleteLater();
return result.object().toVariantMap();
}
disconnect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
reply->abort();
reply->deleteLater();
return QVariantMap();
}
Is that a good way to proceed? How should I manage the QNetworkReply pointer when other signals are emitted (i.e. error(), sslErrors(), ...)?
QNetworkReply will always emit finished(), even when an error occured.
deleteLater() could even be called in a slot connected to that signal, so that part should be fine.
But I would recommend to look into a more asynchronous approach of handling the request, nested event loops like the on in your getResponse() can lead to "interesting" behavior, because you can basically get into re-entrancy situations in a single threaded program.

QNetworkAccessManager does not emit signal

So I have this code:
QUrl url("http://...");
QNetworkRequest request(url);
QNetworkReply *reply = m_networkManager->get(request);
connect(reply, SIGNAL(finished()), SLOT(onRequestCompleted()));
connect(reply,SIGNAL(error(QNetworkReply::NetworkError)),SLOT(onError(QNetworkReply::NetworkError)));
and I cant get signal to the other fuction
void IpResolver::onRequestCompleted()
{
QString webContent;
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
if (reply)
{
if (reply->error() == QNetworkReply::NoError)
{
QString webContent = reply->readAll();
}
}
}
I cant figure out the solution, help please.
I don't know what exactly you want, but:
Why do you use reply pointer instead of some kind onRequestCompleted(QNetworkReply *reply)?
If you do so:
QUrl url("http://...");
QNetworkRequest request(url);
connect(m_networkManager, &QNetworkAccessManager::finished, this, &IpResolver::onRequestCompleted);
m_networkManager->get(request);
And your slot will be, for example:
void IpResolver::onRequestCompleted(QNetworkReply *reply)
{
QString webContent;
if (reply->error() == QNetworkReply::NoError)
webContent = reply->readAll();
}

Getting a page content with Qt

I am trying to get the content of a HTTP request into a QString variable with Qt and C++
QNetworkAccessManager networkManager;
QUrl url("https://someurl.test.com/this-actually-exists");
QNetworkRequest request;
request.setUrl(url);
QNetworkReply* currentReply = networkManager.get(request); // GET
QString reply = QTextCodec::codecForMib(1015)->toUnicode(currentReply->readAll());
Still, the variable reply seems to stay empty. Obviously, I misunderstand the documentation. How do I get this to perform?
You can use two different ways even the synchronous or asynchronous ways to do this. The asynchronous way is :
connect (&networkManager , SIGNAL(finished(QNetworkReply*)) ,this, SLOT(done(QNetworkReply*)));
networkManager.get(request);
And you should read the contents from the returned reply in the slot connected to finished signal in the following way :
void net::done(QNetworkReply * reply)
{
if (reply->error() == QNetworkReply::NoError)
{
data = QString(reply->readAll ());
}
else
{
data = QString(reply->errorString ());
}
}
The synchronous way is like :
QNetworkReply *reply = networkManager.get(request);
QEventLoop loop;
connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), &loop, SLOT(quit()));
loop.exec();
QByteArray bts = reply->readAll();
QString str(bts);
Here you use an event loop to wait until the reply is finished and then read the available bytes and get the string.
I need to assume you're running an application with an event-loop in place? If not, then it's a bit harder...
If so, replace your last line that builds the reply QString:
connect(currentReply, SIGNAL(finished()), this, SLOT(gotAReply()));
Then you'll have to define another method in your class as a slot that gets triggered as soon as that reply got filled:
void gotAReply()
{
QNetworkReply *reply = qobject_cast<QNetworkReply*>(QObject::sender());
if (reply)
{
if (reply->error() == QNetworkReply::NoError)
{
QString replyText( reply->readAll() );
}
reply->deleteLater();
}
}
Don't forget: for Signals and Slot to work your class declaration must contain the Q_OBJECT macro.