GOPHERSPACE.DE - P H O X Y
gophering on raymii.org
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 e
xamples print the output of a HTTP GET request on screen after pressing a button. The Qml method uses JavaScript, so that's che
ating a bit, the other method uses plain C++ with Qt's libraries for networking (`QNetworkAccessManager`) and signals and slots
 for the async part.


 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:


 ://leafnode.nl">I'm developing an open source monitoring app called  Leaf Node Monitoring, for windows, linux & android. Go che
ck it out!

 Consider sponsoring me on Github. It means the world to
 me if you show your appreciation and you'll help pay the server costs.

 ode=7435ae6b8212">You can also sponsor me by getting a Digital Ocean VPS. With this referral link you'll get $100 credit for 60
 days. 

 




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 

    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("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 `` 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(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 `` 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(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 h
old 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 .

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.