This is a text-only version of the following page on https://raymii.org: --- Title : HTTP GET requests with Qt and in Qml (async) Author : Remy van Elst Date : 29-04-2022 URL : https://raymii.org/s/tutorials/HTTP_GET_requests_in_Qt_and_Qml_async.html Format : Markdown/HTML --- With Qt it's very easy to work with (async) HTTP requests. This guide shows you how to do it with Qt core and in Qml. The two examples print the output of a HTTP GET request on screen after pressing a button. The Qml method uses JavaScript, so that's cheating a bit, the other method uses plain C++ with Qt's libraries for networking (`QNetworkAccessManager`) and signals and slots for the async part. <p class="ad"> <b>Recently I removed all Google Ads from this site due to their invasive tracking, as well as Google Analytics. Please, if you found this content useful, consider a small donation using any of the options below:</b><br><br> <a href="https://leafnode.nl">I'm developing an open source monitoring app called Leaf Node Monitoring, for windows, linux & android. Go check it out!</a><br><br> <a href="https://github.com/sponsors/RaymiiOrg/">Consider sponsoring me on Github. It means the world to me if you show your appreciation and you'll help pay the server costs.</a><br><br> <a href="https://www.digitalocean.com/?refcode=7435ae6b8212">You can also sponsor me by getting a Digital Ocean VPS. With this referral link you'll get $100 credit for 60 days. </a><br><br> </p> This guide is written mainly because I find myself doing this often and keep looking in other projects where I've already did this to copy over the code. Even my fellow colleagues over at work peek at my GitHub for this specific thing, I was told recently, so better put it up online. ![screenshot][1] > Screenshot of the demo app layout Without using Qt, I'd probably handle network requests using `curl` or something like [cpp-httplib][3], a header-only http client/server. I've done plain old C++ network http requests before and written about it [here, on parsing HackerNews and Lobste.rs API's][4]. The full code for this guide can be found [on my github][9]. ### Basic setup Using Qt Creator, do a `File`, `New Project`. Select an empty Qt Quick (QML) application and finish the wizard. I'm using Qt 5.15, but the example also works with Qt 6.3. This is the `main.qml` file layout, 2 rows with a button and a textfield: Column { spacing: 5 anchors.fill: parent anchors.margins: 5 Row { spacing: 5 Button { text: "Qml HTTP GET" } TextField { id: qmlResult } } Row { spacing: 5 Button { text: "C++ HTTP GET " } TextField { id: cppResult } } } ### C++ HTTP GET Request The plain old C++ HTTP Get uses a few Qt provides classes, namely `QNetworkAccessManager`, `QNetworkRequest` and `QNetworkReply`, including a few signals and slots to handle the request async. We'll start by doing some busywork, creating the class derived from QObject and registering it for the QML Engine. If you've done any Qt before, you know that you'll do this many times and as I do, consider it busywork. Whichever form of `qRegister`/`qmlRegister` you need depends on the shape of the moon, but Qt 6 has made improvements on that spectrum, now using cmake and only 1 place to register objects. #### Create classes and Qml registration Make a new class named `NetworkExample` based off QObject, either by creating the files yourself or by using the Qt Creator `Add New` wizard, in that case select a new C++ class and give it QObject as base: ![add new class wizard][2] **NetworkExample.h** #ifndef NETWORKEXAMPLE_H #define NETWORKEXAMPLE_H #include <QObject> class NetworkExample : public QObject { Q_OBJECT public: explicit NetworkExample(QObject *parent = nullptr); signals: }; #endif // NETWORKEXAMPLE_H **NetworkExample.cpp** #include "NetworkExample.h" NetworkExample::NetworkExample(QObject *parent) : QObject{parent} { } The file does not do anything yet. In `main.cpp`, create an instance and register it to the Qml engine so we can import it in Qml: #include "NetworkExample.h" [...] // below the QGuiApplication line NetworkExample* networkExample = new NetworkExample(); qmlRegisterSingletonInstance<NetworkExample>("org.raymii.NetworkExample", 1, 0, "NetworkExample", networkExample); At the bottom of the file, change the `return app.exec()` line so we save that value but also destroy our object before quitting: auto result = app.exec(); networkExample->deleteLater(); return result; Even though this is a simple example, I'm hoping to teach you a bit of hygiene by explicitly adding this part. In `main.qml`, below the other `import` lines: import org.raymii.NetworkExample 1.0 #### Network request Finally, time to do the actual request. Add the `<QNetworkAccessManager>` header to your includes and add a `QNetworkAccessManager* _manager = nullptr;` in the `private:` section of your header. Inside the constructor, `new` it: _manager = new QNetworkAccessManager(this); Since we're providing a parent object, `new` is fine. Once the parent `QObject` is destroyed, this one will also be destroyed. Add a method to do the actual request. In your header, declare and mark it as `Q_INVOKABLE` so Qml can call it: Q_INVOKABLE void doGetRequest(const QString& url); The function definition: void NetworkExample::doGetRequest(const QString& url) { setResponse(""); auto _request = QScopedPointer<QNetworkRequest>(new QNetworkRequest()); _request->setUrl(url); _request->setTransferTimeout(5000); _request->setRawHeader("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:99.0) Gecko/20100101 Firefox/99.0"); QNetworkReply *reply = _manager->get(*_request); QObject::connect(reply, &QNetworkReply::finished, this, &NetworkExample::slotFinished); } Don't forget to include the `<QNetworkReply>` header. First part is a Qt style smart pointer, so we don't have to delete that `QNetworkRequest` ourselves. Once it goes out of scope, it is destroyed. The very first line clears out any previous response data in our `Q_PROPERTY`, we will define that later. Next up we set a few parameters, most important one being the URL, and as a bonus I've included setting a user agent header and request timeout of 5 seconds. Using our `QNetworkAccessManager` we send the request off, then connecting up the `finished` signal to out reply. To keep this guide simple, I'm not connecting up the `errorOccured` or `readyRead` signals, but you probably should [read the docs][5] regarding the signals `QNetworkReply` can emit. Add a new slot (regular method, below the line `public slots:`) for our `slotFinished` method: public slots: void slotFinished(); Contents: void NetworkExample::slotFinished() { QNetworkReply *reply = dynamic_cast<QNetworkReply*>(sender()); if(reply != nullptr) { setResponse(reply->readAll()); reply->deleteLater(); } } Every `signal/slot` connection has method that returns a pointer to the object that [sent the signal][6], `QObject::sender()`. I'm using it with a `dynamic_cast` to make sure it's not a nullptr and the correct type. Using `QNetworkReply::readAll()`, the entire reply is available. If `slotFinished ()` is called directly (not via a signal/slot), the `reply` object will be a nullptr. There are a few more considerations to keep in mind with `QObject::sender()` like if the origin object is destroyed and `DirectConnection`, but for our example this will work just fine. The [documentation][5] mentions explicitly to call `deleteLater()` on the networkReply, so we do that instead of regular delete. The last part of our method is a new `Q_PROPERTY` named `response`. Add it in the header just below the line `Q_OBJECT`: Q_PROPERTY(QString response READ response WRITE setResponse NOTIFY responseChanged) In recent versions of Qt Creator you can right-click the `Q_PROPERTY` part and select `Refactor`, `Generate Missing Q_PROPERTY Members`. Do that, nothing special about this property otherwise. If your Qt Creator version does not show that handy option, add the signal/slot and member variable yourself manually. In Qml, bind this property to the `TextField` `text` property: TextField { id: cppResult text: NetworkExample.response } Make the `Button` call the function we've just defined: Button { text: "C++ HTTP GET " onClicked: NetworkExample.doGetRequest("http://httpbin.org/ip") } This URL will send back a JSON response containing the sending IP. Press the big green Play (run) button and test it out: ![cpp get request][7] That was easy right? No messing around with a `CURL*` or `curl_easy_setopt()` and async by default. The QML / JavaScript part is even easier, so easy it feels like type-unsafe cheating. ### QML HTTP GET request The QML part is just plain old JavaScript with a property binding. In the `main.qml` file, define a `property var` which will hold the response data, inside the `Window{}`, just above our `Column`: property var response: undefined Right below the new property, add a function that will do the request: function doGetRequest(url) { var xmlhttp = new XMLHttpRequest() xmlhttp.onreadystatechange = function () { if (xmlhttp.readyState === XMLHttpRequest.DONE && xmlhttp.status == 200) { response = xmlhttp.responseText } } xmlhttp.open("GET", url, true) xmlhttp.send() } The method, when called, does a `XMLHttpRequest`, with a callback function that checks the status code, if the request was successful it updates the `response` property. Bind the response property to our `TextField`: TextField { id: qmlResult text: response } Add the new function to the Button's `onClicked`: Button { text: "Qml HTTP GET" onClicked: { response = "" doGetRequest("http://httpbin.org/ip") } } Go forth, press the big green Play button and test it out: ![qml get][8] You could of course, in the case of JSON, add a `JSON.parse(xmlhttp.responseText)`, then you can access the JSON right inside QML, (`text: response.origin`), or add more error handling. As you can see, because it's just JavaScript, this is even easier than the already very simple C++ part. If you want to test the `async`-ness, specifically, not blocking the GUI thread, use the url `https://httpbin.org/delay/4`, which will wait 4 seconds before responding. You should still be able to click the buttons and see stuff happening. Please send me your thoughts regarding what you like best, C++ or Qml for this purpose. [1]: /s/inc/img/network_qt_1.png [2]: /s/inc/img/network_qt_2.png [3]: https://github.com/yhirose/cpp-httplib [4]: /s/software/Cpp_exercise_in_parsing_json_http_apis_and_time_stuff.html [5]: https://web.archive.org/web/20220428080921/https://doc.qt.io/qt-5/qnetworkreply.html#errorOccurred [6]: https://web.archive.org/web/20220427082244/https://doc.qt.io/qt-5/qobject.html#sender [7]: /s/inc/img/network_qt_3.png [8]: /s/inc/img/network_qt_4.png [9]: https://github.com/RaymiiOrg/qt-http-get-request-qml-cpp --- License: All the text on this website is free as in freedom unless stated otherwise. This means you can use it in any way you want, you can copy it, change it the way you like and republish it, as long as you release the (modified) content under the same license to give others the same freedoms you've got and place my name and a link to this site with the article as source. This site uses Google Analytics for statistics and Google Adwords for advertisements. You are tracked and Google knows everything about you. Use an adblocker like ublock-origin if you don't want it. All the code on this website is licensed under the GNU GPL v3 license unless already licensed under a license which does not allows this form of licensing or if another license is stated on that page / in that software: This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. Just to be clear, the information on this website is for meant for educational purposes and you use it at your own risk. I do not take responsibility if you screw something up. Use common sense, do not 'rm -rf /' as root for example. If you have any questions then do not hesitate to contact me. See https://raymii.org/s/static/About.html for details.