I'm having quite a bit of struggle in trying to achieve a simple PUT request in QT. Frankly I am still a starter using this framework so I have much left to learn.
What I am trying to do is make a PUT request with 2 parameters this Cloudflare Workers KV API.
With that being said this is my current code snippet:
QString szUrl = "";
QHttpMultiPart* multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QHttpPart valuePart;
valuePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"value\""));
valuePart.setBody(value.toByteArray());
QHttpPart metadataPart;
metadataPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"metadata\""));
metadataPart.setBody(metadata.toByteArray());
multiPart->append(valuePart);
multiPart->append(metadataPart);
QNetworkRequest request;
request.setUrl(szUrl);
request.setHeader(QNetworkRequest::ContentTypeHeader, "multipart/form-data");
request.setRawHeader("Authorization", QString("Bearer %1").arg("...").toUtf8());
QNetworkReply* reply = m_NetworkManager->put(request, multiPart);
// delete the multiPart with the reply
multiPart->setParent(reply);
// Process reply (QNetworkAccessManager::finished)
// Process errors (QNetworkAccessManager::sslErrors)
qDebug() << reply->error();
qDebug() << reply->readAll();
qDebug() << reply->errorString();
The above returns always HTTP 400 (bad request) with zero, and I mean ZERO response. I could not retrieve the error response from the API in any way. Just an empty string ("") under debug.
If I execute the example cURL cli code from the API page, it works perfectly. I get a response and it's successful.
My question would be, what on earth am I doing wrong? I read the documentation and wrote the code accordingly, I cannot understand what's happening.
I have searched quite a bit for a possible answer online but unfortunately all issues and examples are with POST requests and file uploads (mostly).
Please advise, it would be much appreciated.
I will answer my own question because one of the reasons the above was not working it's because of my own fault.
Problem 1:
Bad formatting of metadata. Looks like Cloudflare API wants the metadata "compressed".
Problem 2:
Unable to retrieve API error response. Now this is an interesting one because I found countless forum posts and posts even here with the same problem, yet nobody posted the actual solution.
The readAll() function states this issue:
This function has no way of reporting errors; returning an empty
QByteArray can mean either that no data was currently available for
reading, or that an error occurred. This function also has no way of
indicating that more data may have been available and couldn't be
read.
The resolution is to connect QIODevice::readyRead to as per the documentation:
connect(reply, &QIODevice::readyRead, this, [=]() {
QByteArray response = reply->readAll();
qDebug() << response;
});
You now have the server reply regardless of the HTTP error code.
With that being said 2 problems solved in one go, enjoy.
Related
I am trying to write a simple free translator (QT widget) using the online translation service.
The idea is to send the standard get request to an online translator, and then parse the response.
But the reply does not contain the translated text ! I guess this is because the service uses AJAX.
In the example, I am using the google translator, but I get similar results with other translators (yandex, deepl).
I know that there is a way to use the shareware API, but since the project is not commercial at the moment, I do not want to register a bank card.
Is there a browser-like way to get translation without the API and use it for free ?
I have searched for any information, but to my surprise, it was outdated and irrelevant at the moment (since Google closed the free service).
And one more question. When I tried to cast the result to a QString (QString s = reply->readAll().toString() or QString s = reply->readAll().toStdString().c_str()), I got a distorted htlm code (a lot of NUL characters at the beginning of the file). I assume that this is due to a misinterpretation of the escape sequences, but how then to cast the result correctly ? Even in the current version, there is some garbage at the beginning of the file (NUL NUL Уi).
The code I use is:
void getTranslate() {
QNetworkAccessManager manager;
QUrl url("https://translate.google.com/#view=home&op=translate&sl=en&tl=ru&text=Hello%2C%20World%20!");
QNetworkRequest request(url);
QNetworkReply *reply = manager.get(request);
do {
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
} while(!reply->isFinished());
QFile html("out.html");
if (html.open(QIODevice::ReadWrite)) {
QDataStream out(&html);
out << reply->readAll();
}
reply->close();
delete reply;
}
looking at Google Translate it uses AJAX Request, to get the translation. You could try to change the URL to something like this (this is where the ajax request goes to):
https://translate.google.de/translate_a/single?client=webapp&sl=auto&tl=en&hl=de&dt=at&dt=bd&dt=ex&dt=ld&dt=md&dt=qca&dt=rw&dt=rm&dt=sos&dt=ss&dt=t&dt=gt&otf=2&ssel=0&tsel=0&xid=45662847&kc=1&tk=656516.836633&q=dies%20ist%20ein%20test
This request returns JSON data, which should be easy to parse.
I am not sure what all the parameters are for, but maybe this information is helpful for you.
I belive your problems with screen-scraping approach may be that the translate application uses Ajax to call the server-side and retrieve the translation. The page you get when downloading using QNetworkRequest is merely the JS application, it doesn't actually contain the translation. That doesn't get filled in until after a call has been made from the page to the server.
And that is why it isn't working. Perhaps you could get it working somehow, so let us know how you do it :-)
I have one URL something as below:
QUrl url("https://example.com/send/?apikey=somekey&numbers=mobile&sender=XYZ&message=0000%20is%20your%20OTP%20to%20authenticate%20with%20Server:%20abc.com")
Now I call this URL as below:
QNetworkRequest networkRequest(url);
QNetworkAccessManager manager;
QEventLoop event;
QObject::connect(&manager, &QNetworkAccessManager::finished, &event, &QEventLoop::quit);
auto* const pResponse = manager.get(networkRequest);
event.exec();
pResponse->deleteLater();
return pResponse->readAll();
However, the call doesn't succeed. Upon debugging I found out that, the "&message" part from URL which is
&message=0000%20is%20your%20OTP%20to%20authenticate%20with%20Server:%20abc.com
becomes
&message=0000 is your OTP to authenticate with Server: abc.com
Probably that's the cause of the failure. If I copy paste the URL in the browser, it works fine.
How to make this request a success?
Though I have coded as of it's a blocking call, actually my requirement is to just invoke the above URL.
Update: This problem is fixed by itself. The website, where I was making an API call, was following certain template. I was missing a fullstop "." at the end. Upon adding that, it started working.
I'm trying to send a bit of json data to a web service using Qt 5.1.1. There are many examples out there, but almost all are for Qt 4.x, which had a slightly different API. Here's what I'm trying now:
QUrl url("http://...");
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
QByteArray jsonInPostFormat = "json=" + QUrl::toPercentEncoding(jsonAsString);
jsonInPostFormat.replace("%20","+");
reply = net->post(request, jsonInPostFormat);
connect(reply, SIGNAL(finished()), this, SLOT(finishedAddComment()));
The above has ... no effect. If I'm doing things correctly, I seem to not get any response from the server. (I call reply->readALL() in finishedAddComment().)
The service has a test form that works correctly. I've dumped the headers that it's sending and looked at the html, which is just a standard form with post method specified.
Am I doing something obviously wrong? Is there a better way, say, using QUrlQuery?
Some way of seeing the raw HTML requests that Qt is actually sending would be super-helpful...
Thanks!
Tyler
Qt5 no longer has the QUrl::encodedQuery() method. Not sure, but from the documentation it might work using QUrl::query() method instead.
Hope it helps.
i need to check the if file exists on http server,
i have the full path and when i try it via browser all works
but when i try in code to do :
if(QFile::exists("http://www.foo.com/hidden/Support/myapp_1.1.2_installer.exe" ))
{
qDebug("file exists");
return true;
}
else
{
qDebug("file not exists");
}
as it writen here :
http://www.qtcentre.org/archive/index.php/t-43712.html?s=b9ae49962c9219aec93b43c514e2ba33
it allways returns me false no matter what ..
what im doing wrong and is it the right way to do this ?
The function QFile::exists is not able to create HTTP requests, which would be necessary to achieve what you are trying to do. The forum discussion you linked to works, because the guy is trying to access a network drive; this is naturally supported by the operating system.
To check whether the file exists, you will have to go the long way around - here is an explanation of how to communicate with a web server: http://developer.nokia.com/Community/Wiki/Creating_an_HTTP_network_request_in_Qt
The Qt class QFile can only deal with files on local filesystem.
You can try out using Qt Network module, probably like this:
QNetworkAccessManager *nam = new QNetworkAccessManager(this);
....
QNetworkRequest req(QUrl("http://www.foo.com/hidden/Support/myapp_1.1.2_installer.exe"));
QNetworkReply *reply = nam->get(req);
connect(reply, SIGNAL(metaDataChanged()),
this, SLOT(slotMetaDataChanged()));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(slotNetworkError(QNetworkReply::NetworkError)));
NOTE THAT if you only want to check for the file's existence, you DON'T want to connect to the finished(QNetworkReply*) signal, because the signal will only be emitted when the network reply has finished processing. That is, the signal will only be emitted after the file is totally downloaded if the file exists.
Then,
slotMetaDataChanged() is called whenever you received new HTTP response headers, you can then check the QNetworkRequest::HttpStatusCodeAttribute for response HTTP codes like 200(OK) or 404(Not Found). In your case, if the returned HTTP code is 200, the file exists.
slotNetworkError() is called when the network request encounters an error, like "Host Not Found" or "Connection Refused", it's up to you to handle these situations in this slot.
The way you are trying to do this, is totally wrong. QFile isn't able to query a webserver. What you need to do is use the QNetworkAccessManager class. With this you can try to download your myapp_1.1.2_installer.exe. If the file does not exist, you will get an error message.
Why the poster on qtcente.org claims it worked for him... no idea. Maybe because his address was a local one. But it still smells fishy.
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)),
this, SLOT(replyFinished(QNetworkReply*)));
manager->head(QNetworkRequest(QUrl("www.foo.com/hidden/Support/myapp_1.1.2_installer.exe")));
The QNetworkReply in the replyFinished slot has the method NetworkError QNetworkReply::error() const.
You should get a QNetworkReply::ContentNotFoundError if your file does not exist.
Edit: As several comments pointed out, just to learn the existence of a file on a remote server using 'get' and connecting to replyFinished might not be the best of ideas. Might be ok for very small files, but definitely overkill for large blobs of data. I changed the 'get' request into a 'head'. Turner's solution will work, mine should now be an acceptable alternative.
First off, I would like to say I'm totally new to BB and I'm coming from an Android background.
I've been looking at samples such as:
https://developer.blackberry.com/cascades/documentation/device_comm/networking/
I have an application which is making a lot of different (and similar) web requests. How do I identify these incoming replies so I can demux them to their appropriate components? Can I tag them somehow?
Thanks and please let me know if I can be more clear.
As #Kernald wrote above, all the information you're likely requesting could be found in QNetworkReply object. You get pointer to this object after placing a request by calling QNetworkAccessManager::get() or QNetworkAccessManager::put()
When you get the reply it's delivered via QNetworkAccessManager::finished(QNetworkReply *reply) signal
Here you can get access to the counterparts Via pointer to respective QNetworkRequestand it's contents depending on what you're after
QNetworkAccessManager* networkAccessManager;
// skipped
bool result = connect(networkAccessManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(requestFinished(QNetworkReply*)));
Q_ASSERT(result);
// skipped
void requestFinished(QNetworkReply* reply) {
QNetworkRequest* request = reply->request();
QUrl url = request->url(); // get the URL
QVariant header = request->header(); // get the header
// etc...
}
Also, you're able to get raw headers of network reply like that:
QByteArray hdr;
QList<QByteArray> list = reply->rawHeaderList();
Q_FOREACH(hdr, list){
qDebug() << hdr;
}
If it's not enough for some reason, you might manually tag network request by assigning an QNetworkRequest::Attribute to QNetworkRequest object:
QNetworkRequest request; // Create and send the network request
QNetworkRequest::Attribute attr = QNetworkRequest::User+1; // any unique value greater than QNetworkRequest::User
QString myStuff;
request.setAttribute(attr, myStuff);
These attribute values must be greater that QNetworkRequest::User up to QNetworkRequest::UserMax. Afterwards you get the attribute previously assigned to the request in the following way:
void requestFinished(QNetworkReply* reply) {
QNetworkRequest* request = reply->request();
QNetworkRequest::Attribute myAttr = QNetworkRequest::User+1;
QVariant myStuff = reply->request().attribute(myAttr);
// do something further
}
Here is official BB10 and Qt (for the version 4.8 which is currently used at the latest Blackberry 10 SDK) documentation on this:
QNetworkRequest (BB10) | QNetworkRequest (Qt official)
QNetworkReply (BB10) | QNetworkReply (Qt official)
You have at least two ways to do this:
From the QNetworkReply, you can access to your original request (https://developer.blackberry.com/cascades/reference/qnetworkreply.html#request), on which you can set an originatingObject: https://developer.blackberry.com/cascades/reference/qnetworkrequest.html#setOriginatingObject, and any other attribute on your request
You can check against the content URL: https://developer.blackberry.com/cascades/reference/qnetworkreply.html#url