Qt: How to get the response from a GET? - qt

I'm having trouble collecting the response from a web request i do. (Because i'm new to Qt).
Why do i have trouble?
I have a request class which send a request and receive a response. But i can't get the response to the parent of the request object, because i have to wait for a "finished" signal from the NetworkAccessMaanager which handles the response.
So i handle the response in a "finished" slot, but i can't return the info to the parent main window holding the request object. How can i do this?
Here's the code:
Main window.cpp:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_buttonLogin_clicked()
{
request->sendRequest("www.request.com");
}
Request.cpp:
Request::Request()
{
oNetworkAccessManager = new QNetworkAccessManager(this);
QObject::connect(oNetworkAccessManager,SIGNAL(finished(QNetworkReply*)),this,SLOT(finishedSlot(QNetworkReply*)));
}
/*
* Sends a request
*/
QNetworkReply* Request::sendRequest(QString url)
{
QUrl httpRequest(url);
QNetworkRequest request;
request.setSslConfiguration(QSslConfiguration::defaultConfiguration()); // Set default ssl config
request.setUrl(httpRequest); // Set the url
QNetworkReply *reply = oNetworkAccessManager->get(QNetworkRequest(httpRequest));
return reply;
}
/*
* Runs when the request is finished and has received a response
*/
void Request::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)
{
// Reading the data from the response
QByteArray bytes = reply->readAll();
QString jsonString(bytes); // string
bool ok;
QVariantMap jsonResult = Json::parse(jsonString,ok).toMap();
if(!ok)
{
qFatal("An error occured during parsing");
exit(1);
}
// Set the jsonResult
setJsonResult(jsonResult);
}
// Some http error received
else
{
// handle errors here
}
// We receive ownership of the reply object
// and therefore need to handle deletion.
delete reply;
}
/*
* Set the json result so that other functions can get it
*/
void Request::setJsonResult(QVariantMap jsonResult)
{
m_jsonResult = jsonResult;
}
/*
* Get the json result
* Return null if there is no result
*/
QVariantMap Request::getJsonResult()
{
return m_jsonResult;
}
Any ideas of how i can do this?
Thanks in advance!

Each QNetworkReply emits finished() signal, so you should connect signal from QNetworkReply* returned by request->sendRequest("www.request.com"); to slot of MainWindow.
EXAMPLE:
void MainWindow::on_buttonLogin_clicked()
{
QNetworkReply *reply = request->sendRequest("www.request.com");
connect(reply, SIGNAL(finished()), this, SLOT(newslot()));
}
void MainWindow::newslot()
{
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
// there you can handle reply as you wish
}

Related

Why file name is not set when uploaded to google drive?

My Objective
I want to upload a sqlite file to google drive.
Although I have been able to achieve uploading the file to google drive, the file uploaded name is being "Untitled". I tried Performing Multipart Upload with the help of Qt's QHttpMultiPart.
Here is my code
bool File_Control::Upload_File_Multipart(const QString &FileName, const QByteArray &rawData)
{
if(FileName.isEmpty()){
emit setMessage("Error: File Name can not be empty");
return false;
}
if(rawData.size()==0){
emit setMessage("Error: File size can not be zero");
return false;
}
// Prepare MetaDataPart
QHttpPart MetadataPart;
MetadataPart.setRawHeader("Content-Type", "application/json; charset=UTF-8");
QString Body;
Body = "{\n"
+ tr("\"name\": \"%1\"\n").arg(FileName)
+ tr("}");
MetadataPart.setBody(Body.toUtf8());
// Prepare MediaPart
QHttpPart MediaPart;
MediaPart.setRawHeader("Content-Type", "application/octet-stream");
MediaPart.setBody(rawData);
// Now add MetaDataPart and MediaPart together to form multiPart
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::RelatedType);
multiPart->setBoundary("bound1");
multiPart->append(MetadataPart);
multiPart->append(MediaPart);
// Now Prepare the request
QUrl url("https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart");
QNetworkRequest request(url);
QString AccessTokenBearer = "Bearer " + mAccess_Token; // mAccess_Token holds the access_token for the session
request.setRawHeader("Content-Type","Content-Type: multipart/related; boundary=bound1");
request.setRawHeader("Authorization", AccessTokenBearer.toUtf8());
// Get Reply and connect it to set Value
QNetworkReply *reply = mQNAM.post(request, multiPart); // mQNAM is QNetworkAccessManager
QObject::connect(reply, SIGNAL(uploadProgress(qint64,qint64)),
this, SIGNAL(setValue(qint64,qint64))); // Update in gui
// Set timeout mechanism while waiting for reply finished
bool stop = false;
QTimer timer;
timer.setSingleShot(true);
QObject::connect(reply, &QNetworkReply::uploadProgress, [&](){
timer.stop();
timer.start(TIMEOUT); // Restart timer every time upload progress
});
QObject::connect(&timer, &QTimer::timeout, [&](){
stop = true;
});
timer.start(TIMEOUT);
// Wait till the response is completed
while(!reply->isFinished()){
QCoreApplication::processEvents();
if(stop){
reply->abort();
}
}
// Check reply
if(reply->error() != QNetworkReply::NoError){
if(reply->error() == QNetworkReply::OperationCanceledError){
emit setMessage("Error: Timeout");
}
else{
emit setMessage("Error: "+reply->errorString());
}
delete reply;
return false;
}
else{
QByteArray rawData = reply->readAll();
qDebug()<<"Size of reply "<<rawData.size();
qDebug()<<"Data "<<rawData;
delete reply;
return true;
}
}
Edit 1
Body.utf8 log -> "{\n\"name\": \"db_06_08_2018_16_18_11_979.sqlite\"\n}"
I finally found the mistake. It was nothing but a typo in preparing the request
Before:
request.setRawHeader("Content-Type","Content-Type: multipart/related; boundary=bound1");
After:
request.setRawHeader("Content-Type","multipart/related; boundary=bound1");
This solved the issue.

Qt , QMutex: destroying locked mutex then app crash

I make an http operation(get,post etc...) by using QNetworkAccessManager. I run a few "get" operation in paralel. For this , I use QtConcurrent::run(this,&RestWebservice::GetHTTPData) to make multi HTTP operations.
My problem is When I close the app before HTTP operation does not complete , App is crashed.Application Output write this line QMutex: destroying locked mutex then write The program has unexpectedly finished.
I guest problem occurs in this line
void RestWebservice::get()
{
// mutex.lock();
m_networkManager.get(m_networkrequest);
// mutex.unlock();
}
But I am not sure because QtCreater Debugger is not good like VS.By the way , GetHTTPData is in different class.
MY CODE for start network Operation:(MobileOperation.cpp).For exapmle getUserAccount metod start a http operation.
void MobileOperations::getWorkOrderListT(int ekipId) {
MyGlobal::Metods metod=MyGlobal::EkipIsEmriListesi;
QString parameters="{EkipId}";
QMap<QString,QVariant> paramlist;
paramlist["EkipId"]=ekipId;
GetHTTPData(metod,parameters,paramlist);
if(m_workorder.IsSuccess==true)
{
// emit successupdatewo();
if(m_workorder.workorders.count()>0)
{
InsertWo(json.workorder->workorders);
emit processstop("İş Emri Listesi Güncellendi");
// QThread::sleep(2);
}
else
{
emit processstop(json.workorder->ReturnMessage);
}
emit successworkstart();
}
else
{
emit processstop("Bağlantı Başarısız Oldu");
}
}
void MobileOperations::getUserAccount(QString kullaniciAdi, QString sifre,bool isremember)
{
json.user=m_user;
QtConcurrent::run(this,&MobileOperations::getUserAccountT,kullaniciAdi,sifre,isremember);
// getUserAccountT(kullaniciAdi,sifre,isremember);
processstart("Baglaniyor");
}
void MobileOperations::GetHTTPData(MyGlobal::Metods MetodName, QString Parameters, QMap<QString, QVariant> paramlist)
{
try
{
parameter=new HttpRequest();
parameter->url=m_url;
parameter->metodname=MetodName;
parameter->resource=m_path;
parameter->appid=m_appid;
parameter->apppass=m_apppass;
parameter->parametersname=Parameters;
parameter->params=paramlist;
rest= new RestWebservice(parameter->GenerateHTTPQuery(),MetodName);
// json=new JSonParser();
// loop=new QEventLoop();
loop=new QEventLoop();
QObject::connect(rest,SIGNAL(sendhttpdata(QByteArray,MyGlobal::Metods)),&json,SLOT(onGetData(QByteArray,MyGlobal::Metods)));
QObject::connect(&json,SIGNAL(serilazitionCompleted()),loop,SLOT(quit()));
rest->get();
loop->exec();
}
catch(std::string &exp)
{
qDebug()<<"Sonlandırıldı";
}
}
MY CODE of classes For HTTP operatins :
#include "restwebservice.h"
#include <QJsonDocument>
#include<QJsonArray>
#include <QJsonObject>
#include<QJsonValue>
#include<QList>
#include <QThread>
RestWebservice::RestWebservice(QNetworkRequest request,
MyGlobal::Metods metod,
QObject* parent):QObject(parent),m_networkrequest(request),m_metodname(metod)
{
connect(&m_networkManager, SIGNAL(finished(QNetworkReply*)),this, SLOT(onResult(QNetworkReply*)));
// connect(&m_networkManager,SIGNAL())
}
void RestWebservice::get()
{
// mutex.lock();
m_networkManager.get(m_networkrequest);
// mutex.unlock();
}
void RestWebservice::post(QString request)
{
QByteArray requestA= request.toUtf8();
m_networkManager.post(m_networkrequest,requestA);
}
void RestWebservice::onResult(QNetworkReply* reply)
{
try
{
if (reply->error() != QNetworkReply::NoError)
{
qDebug()<<reply->error()<<":"<<reply->errorString();
MyGlobal::NetworkStatus=reply->errorString();
emit sendhttpdata(m_data,m_metodname);
return;
// throw(reply->errorString().toStdString());
}
QByteArray data = reply->readAll();
reply->deleteLater();
m_data=data;
MyGlobal::NetworkStatus="Tablolar Yüklendi";
emit sendhttpdata(m_data,m_metodname);
}
catch(std::string exp)
{
qDebug()<<"Exception:"<<QString::fromStdString(exp);
}
catch(std::exception &exp)
{
qDebug()<<"Exception:"<<QString::fromStdString(exp.what());
}
}
void RestWebservice::onError()
{
qDebug()<<"Hata VAR";
}
HttpRequest::HttpRequest(QObject *parent) :
QObject(parent)
{
}
QNetworkRequest HttpRequest::GenerateHTTPQuery()
{
// QString path="";
QString path=QString("/%1/%2/%3/%4/%5").arg(resource).arg(MyGlobal::getMetodName(metodname)).arg(appid).arg(apppass).arg(parametersname);
foreach (QString param, params.keys()) {
path.replace("{"+param+"}",params[param].toString());
}
QUrl m_url(url);
m_url.setPath(path);
m_request.setUrl(m_url);
m_request.setRawHeader("Content-Type","application/json;charset=utf-8");
// m_request.setRawHeader("SOAPAction","http://tempuri.org/IMobileClient/UserAuth");
qDebug()<<m_url.url();
return m_request;
}
QNetworkRequest HttpRequest::GenerateHTTPQueryPost()
{
// QString path="";
QString path=QString("/%1/%2").arg(resource).arg(MyGlobal::getMetodName(metodname));
QUrl m_url(url);
m_url.setPath(path);
m_request.setUrl(m_url);
m_request.setRawHeader("Content-Type","application/json;charset=utf-8");
// m_request.setRawHeader("SOAPAction","http://tempuri.org/IMobileClient/UserAuth");
qDebug()<<m_url.url();
return m_request;
}
Is you mutex a member of your class. In that case the mutex is destructed before it is unlocked (as I presume the containing class goes out of scope), which causes the message you see. The destructor of the mutex is called when the class is destructed, while the lock is held. This is a problem. Typically you will have to find a way to not block indefinitely during your network request.

qt post and get request

I'm trying to log in to a webpage and then download some data that only members can download.
I've performed the post request and then the get request, for some reason the get request was finished first.
Is there any way to wait for the post to be finished or make it done first?
edit:
void downloader::LoginToHost()
{
QNetworkRequest request(QUrl("http://www.example.com/login.php"));
request.setHeader(QNetworkRequest::ContentTypeHeader,QVariant("application/x-www-form-urlencoded"));
QByteArray data("email=example&password=");
manager->post(request,data);
}
void downloader::Do_Request(QUrl url)
{
manager->get(QNetworkRequest(url));
}
void downloader::finished(QNetworkReply * reply)
{
QMessageBox mess;
if(reply->error() == QNetworkReply::NoError)
{
std::ofstream source("source-code.txt");
QString content = reply->readAll();
source << content.toStdString() << std::endl;
source.close();
mess.setText("Done download!");
}
else mess.setText(reply->errorString());
mess.exec();
}
Send the post and don't send the get until the post returns and triggers the finished signal in QNetworkReply.
Assuming there are two requests, one for the post and one for the get, which you've set up, here's an outline of what you can do: -
QNetworkRequst postRequest;
QNetworkRequest getRequest;
Sending the postRequest returns a QNetworkReply...
QNetworkAccessManager* networkAccessManager = new QNetworkAccessManager;
QNetworkReply postReply = networkAccessManager->post(postRequest, someData);
QNetworkReply getReply = nullptr; // C++ 11 nullptr
You can then connect to the postReply finished signal: -
// using a connection to a lambda function, send the get request when post has finished
connect(postReply, &QNetworkReply::finished, [this, networkAccessManager, getRequest, getReply](){
// handle the reply...
...
// send the getRequest
getReply = networkAccessManager->get(getRequest);
});

QNetworkAccessManager does not return any data

I have come across bug that I am not able to see myself. After studing QT and stack sites I wrote following code:
void RateOfExchangeGetter::run(){
mRunning = true;
mAccessManager = new QNetworkAccessManager();
//connect(mAccessManager, SIGNAL(finished(QNetworkReply*)),
// this, SLOT(replyFinished(QNetworkReply*)));
while(mRunning){
QNetworkReply *reply;
for(SiteParser *parser: mSiteParsers){
QNetworkRequest request(QUrl("https://www.google.pl/"));
request.setRawHeader("User-Agent", "MyOwnBrowser 1.0");
qDebug() << "here";
reply = mAccessManager->get(request);
parser->setQNetworkReply(reply);
connect(reply, SIGNAL(readyRead()), parser, SLOT(slotReadyRead()));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
parser, SLOT(slotError(QNetworkReply::NetworkError)));
connect(reply, SIGNAL(sslErrors(QList<QSslError>)),
parser, SLOT(slotSslErrors(QList<QSslError>)));
}
//for test only ->
this->sleep(10);
QByteArray array = reply->read(50);
qDebug() << array;
}
}
As good eye might have already noticed - this code is placed in class that inherits QThread.
For some reason (that I cannot find) I can't receive any data from get function (I know that it is asynchronous), no signals are transmitted, and also after waiting 10 second there are no data available in read. I had also tried to get data via finished signal from QNetworkAccessManager itself but also nothing appeared.
Thanks to anyone who might have a clue what is happening.
You don't have an event loop in your thread's run() method, so there's no chance of any networking code working.
You should put all of your code into a QObject, and move it to a generic thread. The default implementation of QThread::run() spins an event loop.
I also don't see much reason for there to be more than one parser. You can simply let the QNetworkReply accumulate the response in its internal buffer until the request is finished - you can then parse it all in one go, obviating the need for parser state. Use the finished slot instead of readyRead. The readyRead slot only makes sense for large requests.
Below is how it might be done. Note the arming mechanism to prevent races between the worker thread and the main thread. You're guaranteed that the finishedAllRequests signal will be emitted exactly once after a call to arm(). Without this mechanism, the worker thread might be able to process all requests before the connect has a chance to run, and no signal will reach the recipient, or you might get multiple signals as the requests are processed before the next one is added.
#include <QCoreApplication>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QPointer>
#include <QSslError>
#include <QThread>
#include <QMetaMethod>
#include <QDebug>
class Parser : public QObject {
Q_OBJECT
bool m_armed;
QList<QNetworkReply*> m_replies;
QPointer<QNetworkAccessManager> m_manager;
QMetaMethod m_addRequestImpl, m_armImpl;
Q_SLOT void finished() {
QNetworkReply * reply = static_cast<QNetworkReply*>(sender());
Q_ASSERT(m_replies.contains(reply));
qDebug() << "reply" << reply << "is finished";
// ... use the data
m_replies.removeAll(reply);
if (m_armed && m_replies.isEmpty()) {
emit finishedAllRequests();
m_armed = false;
}
}
Q_SLOT void error(QNetworkReply::NetworkError) {
QNetworkReply * reply = static_cast<QNetworkReply*>(sender());
m_replies.removeAll(reply);
}
Q_SLOT void sslErrors(QList<QSslError>) {
QNetworkReply * reply = static_cast<QNetworkReply*>(sender());
m_replies.removeAll(reply);
}
Q_INVOKABLE void addRequestImpl(const QNetworkRequest & req) {
QNetworkReply * reply = m_manager->get(req);
connect(reply, SIGNAL(finished()), SLOT(finished()));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
SLOT(error(QNetworkReply::NetworkError)));
connect(reply, SIGNAL(sslErrors(QList<QSslError>)),
SLOT(sslErrors(QList<QSslError>)));
m_replies << reply;
}
Q_INVOKABLE void armImpl() {
if (m_replies.isEmpty()) {
emit finishedAllRequests();
m_armed = false;
} else
m_armed = true;
}
static QMetaMethod method(const char * signature) {
return staticMetaObject.method(staticMetaObject.indexOfMethod(signature));
}
public:
// The API is fully thread-safe. The methods can be accessed from any thread.
explicit Parser(QNetworkAccessManager * nam, QObject * parent = 0) :
QObject(parent), m_armed(false), m_manager(nam),
m_addRequestImpl(method("addRequestImpl(QNetworkRequest)")),
m_armImpl(method("armImpl()"))
{}
void addRequest(const QNetworkRequest & req) {
m_addRequestImpl.invoke(this, Q_ARG(QNetworkRequest, req));
}
void arm() {
m_armImpl.invoke(this);
}
Q_SIGNAL void finishedAllRequests();
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QThread * thread = new QThread(&a);
thread->start();
QNetworkAccessManager mgr;
Parser parser(&mgr);
mgr.moveToThread(thread);
parser.moveToThread(thread);
for (int i = 0; i < 10; ++i) {
QNetworkRequest request(QUrl("https://www.google.pl/"));
request.setRawHeader("User-Agent", "MyOwnBrowser 1.0");
parser.addRequest(request);
}
thread->connect(&parser, SIGNAL(finishedAllRequests()), SLOT(quit()));
a.connect(thread, SIGNAL(finished()), SLOT(quit()));
parser.arm();
int rc = a.exec();
thread->wait();
delete thread; // Otherwise mgr's destruction would fail
return rc;
}
#include "main.moc"
Output:
reply QNetworkReplyHttpImpl(0x1011619e0) is finished
reply QNetworkReplyHttpImpl(0x101102260) is finished
reply QNetworkReplyHttpImpl(0x101041670) is finished
reply QNetworkReplyHttpImpl(0x1011023e0) is finished
reply QNetworkReplyHttpImpl(0x10102fa00) is finished
reply QNetworkReplyHttpImpl(0x101040090) is finished
reply QNetworkReplyHttpImpl(0x101163110) is finished
reply QNetworkReplyHttpImpl(0x10103af10) is finished
reply QNetworkReplyHttpImpl(0x10103e6b0) is finished
reply QNetworkReplyHttpImpl(0x101104c80) is finished

how can I read the data sent from the server in a variable of type QNetWorkReply?

I have used this code to sent the user name and a password to the server using POST Method.
This returns me a reply,So how can I access the reply variable in the way that i can read the data sent from the server back to me ??
Used Code:
void MainWindow::post(QString name, QString password)
{
QUrl serviceUrl = QUrl("http://lascivio.co/mobile/post.php");
QByteArray postData;
QString s = "param1="+name+"&";
postData.append(s);
s = "param2=" +password;
postData.append(s);
QNetworkAccessManager *networkManager = new QNetworkAccessManager(this);
connect(networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(serviceRequestFinished(QNetworkReply*)));
QNetworkRequest request;
request.setUrl(serviceUrl);
QNetworkReply* reply = networkManager->post(request, postData);
}
void MainWindow::serviceRequestFinished(QNetworkReply* reply)
{
//????????????
}
QNetworkReply is a QIODevice, so you can read it the same way you would read a file. But you have to destroy the QNetworkReply and check for error too in that slot.
For example, in the simplest case (without HTTP redirection):
void MainWindow::serviceRequestFinished(QNetworkReply* reply)
{
// At the end of that slot, we won't need it anymore
reply->deleteLater();
if(reply->error() == QNetworkReply::NoError) {
QByteArray data = reply->readAll();
// do something with data
...
} else {
// Handle the error
...
}
}
You should probably declare the QNetworkAccessManager variable as a member of your class instead of creating a new one for each request.

Resources