qt post and get request - qt

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

Related

QNetworkAccessManager HTTP Put request not working

After an exhaustive search on the web about this issue, none of the answers found solved it.
Using the technology Qt_5_15_2_MSVC2019_64/Microsoft Visual C++ Compiler,
I'm trying to send a JSON file through HTTP put request from QNetworkAccessManager to a custom QTcpServer, but when doing so, only HTTP headers are received (even twice) but not the JSON file in itself.
Snippet Code:
void Sender::putParameters(const QString& p_parameters) {
QJsonDocument docJson = QJsonDocument::fromJson(p_parameters.toUtf8());
QByteArray data = docJson.toJson();
QUrl url= QUrl("http://127.0.0.1:80/api/devices/1285/parameters");
QNetworkAccessManager* nam = new QNetworkAccessManager(this); // Even tried to put as member variable but still did not work
QNetworkRequest networkReq(url);
networkReq.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
networkReq.setRawHeader("Content-Length", QByteArray::number(data.size()));
QObject::connect(nam, &QNetworkAccessManager::finished, this,
&Sender::validateParameters);
nam->put(networkReq, data);
if(docJson.isEmpty())
qDebug() << "JSON was empty";
}
Result QTcpServer:
With Poco library there's no issue it works, or sending HTTP put request via Postman it does work.
#dabbler
Sorry for the late answer but here is the code snippet of the Server :
enter image description here
With postMan and Poco it's working perfectly, so I don't know if the issue is then related to the server, and for the client side #Dmitry you're right I should use QJsonDocument::isNull instead (but I used isEmpty() just for demonstration purpose that I wasn't sending an invalid Json which is not the case, the json is valid).
Yes my bad #Syfer
So I have the class Server inheriting from QTcpServer and overriding function incomingConnection such as : void incomingConnection(qintptr) Q_DECL_OVERRIDE;
Here is the code snippet:
Server::Server(QObject* parent) : QTcpServer(parent)
{
if(listen(QHostAddress::Any, 80))
qDebug() << "Ok listening port 80";
else
qCritical() << "Fail listening";
}
void Server::incomingConnection(qintptr p_intPtrSocket)
{
QTcpSocket* socketClient = new QTcpSocket(this);
socketClient->setSocketDescriptor(p_intPtrSocket);
connect(socketClient, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
}
void Server::onReadyRead()
{
QTcpSocket* socketClient = (QTcpSocket*)sender();
QString fullClientRequest = QString::fromUtf8(socketClient->readAll());
QStringList clientReq = fullClientRequest.split("\n");
qDebug() << "client response: " << clientReq;
}

Server Sent Events (SSE) with Qt

I already saw several libraries for Server Sent Events, unfortunately, not for Qt. I also looked at the specification of SSE (just plain HTTP) and it seems that implementing SSE in Qt would require to:
Use QNetworkAccessManager in streaming mode (download)
Accept the content type header of SSE: application/events-stream
Reconnect when the connection is lost or closed
Attach a slot to the QNAM when new bytes are received (check for data : {...})
I'm not sure if it's so "easy"? Did I miss something?
I created a small demo with Qt and Server Sent Events.
The demo connects to a given EventSource URL (first argument) and prints every event to the command line.
Qt supports SSE out of the box since SSE is pure HTTP with a reconnection layer on top of it.
Prepare the request: set the text/event-stream accept header, allow redirects, disable the cache.
QNetworkRequest Network::Manager::prepareRequest(const QUrl &url)
{
QNetworkRequest request(url);
request.setRawHeader(QByteArray("Accept"), QByteArray(ACCEPT_HEADER));
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); // Events shouldn't be cached
return request;
}
Connect the readyRead signal to a slot.
void Network::Manager::getResource(const QUrl &url)
{
qDebug() << "GET resource:" << url;
QNetworkRequest request = this->prepareRequest(url);
m_reply = this->QNAM()->get(request);
connect(m_reply, SIGNAL(readyRead()), this, SLOT(streamReceived()));
}
Every time a new event is received by the QNetworkAccessManager, you can read it using readAll. We reset the retries counter after every successful event.
void Network::Manager::streamReceived()
{
qDebug() << "Received event from stream";
qDebug() << QString(m_reply->readAll()).simplified().replace("data: ", "");
qDebug() << "-----------------------------------------------------";
m_retries = 0;
}
In case we lost the connection or the connection times out, the finished() signal is triggered of the QNetworkAccessManager. We try to reconnect to the event source (We connected this slot to the signal when we created our QNetworkAccessManager instance):
void Network::Manager::streamFinished(QNetworkReply *reply)
{
qDebug() << "Stream finished:" << reply->url();
qDebug() << "Reconnecting...";
if(m_retries < MAX_RETRIES) {
m_retries++;
this->getResource(reply->url());
}
else {
qCritical() << "Unable to reconnect, max retries reached";
}
}
You can find the demo here: https://github.com/DylanVanAssche/Qt-Server-Sent-Events-Demo

read and write to qtcpsocket using qdatastream

There are various methods of reading and writing from a QTcpSocket using a QDatastream as seen here
The difference is, I will be sending more than "one packet" or blocks.
A basic implementation on the server (sending) side and client (recieving) is seen below - only the actual sending and receiving snippets are shown
More Info, What I tried:
When writing to a QTcpSocket, I attempted to use the QTcpSocket::canReadLine() however this fails straightup after the QTcpSocket::waitForReadReady() signal fires.
I then tried QDataStream::atEnd() in a while loop which causes a system crash :(
The code below shows my latest attempt of going through the QDataStream docs, and utilzing the commitTransaction where it states
If no full packet is received, this code restores the stream to the initial position, after which you need to wait for more data to arrive.
Under the heading Using Read Transactions. But ofcourse, this just reads one block that is sent, i.e the first block.
Question:
When writing to a QTcpSocket multiple times, and flushing the socket each time to send that data, how can I read this from a QTcpSocket as it is send, keep the original "send structure"?
The example below only reads the first block and ends. I would like to read the block containing "Response 2" and "Response 3".
Code Implementations:
//server.h
//...
QTcpSocket *clientSocket = nullptr;
QDataStream in;
//...
//server.cpp
//...
in.setDevice(clientSocket);
in.setVersion(QDataStream::Qt_4_0);
in.startTransaction();
QString nextFortune;
in >> nextFortune;
if (in.commitTransaction())
ui->lblOut->setText(nextFortune);
if (clientSocket != nullptr) {
if (!clientSocket->isValid()) {
qDebug() << "tcp socket invalid";
return;
}
if (!clientSocket->isOpen()) {
qDebug() << "tcp socket not open";
return;
}
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_0);
out << QString(QString("Response:") + nextFortune);
if (!clientSocket->write(block)){
QMessageBox::information(this, tr("Server"),tr("Could not send message"));
}
clientSocket->flush();
// block.clear();
out << QString("Response number 2");
if (!clientSocket->write(block)){
QMessageBox::information(this, tr("Server"),tr("Could not send message"));
}
clientSocket->flush();
// block.clear();
out << QString("Response number 3 here, and this is the end!");
if (!clientSocket->write(block)){
QMessageBox::information(this, tr("Server"),tr("Could not send message"));
}
clientSocket->flush();
clientSocket->disconnectFromHost();
}
//...
And the client side
//client.h
//...
QTcpSocket *tcp_con = nullptr;
QDataStream in;
//...
//client.cpp
//...
if(!tcp_con->waitForReadyRead()){
qDebug(log_lib_netman_err) << "tcp con timeout for reading";
tcp_con->disconnectFromHost();
return ReturnObject(ReturnCode::SocketError, QString());
}
in.setDevice(tcp_con);
in.setVersion(QDataStream::Qt_4_0);
in.startTransaction();
QList<QString> data_rcv = QList<QString>();
QString s;
// while (tcp_con->canReadLine()) {
// in >> s;
// data_rcv.push_back(s);
// }
// while (!in.read) {
in >> s;
data_rcv.push_back(s);
// }
while (!in.commitTransaction()){
qDebug(log_lib_netman_info) << "waiting for more data";
in >> s;
data_rcv.push_back(s);
// qDebug(log_lib_netman_err) << "Unable to send data to server";
// tcp_con->disconnectFromHost();
// return ReturnObject(ReturnCode::FailedReceiving, QString());
}
// if (s.isEmpty()) {
// qDebug(log_lib_netman_err) << "Empty response recieved";
// tcp_con->disconnectFromHost();
// return ReturnObject(ReturnCode::NoDataRecieved, QString());
// }
tcp_con->disconnectFromHost();
return ReturnObject(ReturnCode::ReceivedSuccess, data_rcv);
Help would be greatly appreciated!

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.

Qt: How to get the response from a GET?

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
}

Resources