Qt-ohjelmointi
Qt HTTP

Kun Qt-sovelluksessa halutaan käyttää HTTP-protokollaa, on projektiin lisättävä QtNetwork-moduuli seuraavasti:
qmake:
pro-tiedostoon

QT +=network

CMake:
CMakeLists-tiedostoon
  1. find_package osioon Network
  2. target_link_libraries osioon Network
Tällöin sinulla on jotain tämän kaltaista CMakeLists-tiedostossa (tämä on konsoliprojektista):
find_package(Qt6 6.5 REQUIRED COMPONENTS Core Network)

qt_standard_project_setup()

qt_add_executable(projektin_nimi
    main.cpp
)

target_link_libraries(projektin_nimi
    PRIVATE
        Qt::Core
        Qt::Network
)

QtNetwork on Qt-kirjaston moduuli, joka tarjoaa kehittäjille työkaluja verkko-ohjelmointiin ja verkkoyhteyksien hallintaan Qt-sovelluksissa. Moduuli sisältää muun muassa seuraavat luokat:

LuokkaSelite
QNetworkAccessManagerQNetworkAccessManager on QtNetwork-moduulin luokka, joka tarjoaa pääsyn verkkoresursseihin ja hallinnoi verkkopyyntöjä. Sen avulla voidaan helposti luoda ja käsitellä HTTP-pyyntöjä ja -vastauksia Qt-sovelluksissa.
QNetworkRequestQNetworkRequest on QtNetwork-moduulin luokka, joka edustaa HTTP- tai muuta verkko-pyyntöä.
QNetworkReplyQNetworkReply on QtNetwork-moduulin luokka, josta luodun olion avulla päästään käsiksi http-response dataan.
QJsonDocumentQJsonDocument on Qt:n luokka, jota käytetään JSON-objektien ja -taulukoiden (QJsonObject ja QJsonArray) lukemiseen ja kirjoittamiseen.
QJsonArrayQJsonArray-luokan avulla voidaan käsitellä JSON-array-tyyppistä dataa.
QJsonObjectQJsonObject luokan avulla voidaan käsitellä JSON-object tyyppistä dataa.

HTTP-responsen data luetaan tämän sivun esimerkeissä QByteArray-luokan olioon.

HTTP request
HTTP liikenne toimii asynkronisesti, joten käytetään Signal-Slot systeemiä. Alla lähetetään HTTP GET metodilla request.

Huom! Seuraavissa esimerkeissä manager ja reply ovat luokan jäsenmuuttujia. Ne tulee määritellä header-tiedostossa seuraavasti:

private:
    QNetworkAccessManager *manager;
    QNetworkReply *reply;
Huom! Qt suosittelee, että QNetworkAccessManager luodaan vain kerran sovelluksen elinkaaren aikana, esimerkiksi luokan konstruktorissa.

Ei autentikointia
QString site_url="http://localhost:3000/book";
QNetworkRequest request(site_url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");

//luo manager konstruktorissa
manager = new QNetworkAccessManager(this);
connect(reply, &QNetworkAccessManager::finished, this, &CurrentClassName::getBookSlot);
reply = manager->get(request);
Kun HTTP response saapuu, QNetworkAccessManager emittoi finished signaalin, joka on edellä kytketty getBookSlot-slottiin.
Webtoken autentikointi
Edellinen esimerkki, jos API vaatii webtokenia
QString site_url="http://localhost:3000/book";
QNetworkRequest request(site_url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");

//WEBTOKEN ALKU
    //Onnistuneen loginin seurauksena saadaan arvo muuttujalle webToken, jonka
    //tietotyyppi on QByteArray ja sen eteen asetetaan merkkijono "Bearer "
QByteArray myToken="Bearer "+webToken;
request.setRawHeader(QByteArray("Authorization"),(myToken));
//WEBTOKEN LOPPU

//luo manager konstruktorissa
manager = new QNetworkAccessManager(this);
connect(reply, &QNetworkAccessManager::finished, this, &CurrentClassName::getBookSlot);
reply = manager->get(request);
Huom! Varsinaisen tokenin (eli webToken) eteen on siis kirjoitettava sana Bearer.
Http responsen käsittely

Edellä siis tehtiin http-request ja finished-signaali kytkettiin slottiin getBookSlot. Nyt siis response käsitellään tuossa slotissa.

Responsena JSON-objekti
Mikäli edellä esitetty HTTP response sisältäisi JSON objektin voitaisiin se käsitellä seuraavalla koodilla.
void MainWindow::getBookSlot()
{
    // Tarkistetaan verkkovirheet
    if (reply->error() != QNetworkReply::NoError) {
        qWarning() << "Network error:" << reply->errorString();
        reply->deleteLater();
        manager->deleteLater();
        return;
    }

    QByteArray response_data=reply->readAll();
    // Muunnetaan vastaus QByteArray-tyyppisestä JSON-dokumentiksi
    QJsonDocument json_doc = QJsonDocument::fromJson(response_data);
    // Muunnetaan JSON-dokumentti JSON-objektiksi
    QJsonObject json_obj = json_doc.object();
   // Käsitellään JSON-objektia

    reply->deleteLater();
    manager->deleteLater();
}
Responsena JSON-array
Mikäli edellä esitetty HTTP response sisältäisi JSON-arrayn voitaisiin se käsitellä seuraavalla koodilla.
void MainWindow::getBookSlot()
{
    // Tarkistetaan verkkovirheet
    if (reply->error() != QNetworkReply::NoError) {
        qWarning() << "Network error:" << reply->errorString();
        reply->deleteLater();
        manager->deleteLater();
        return;
    }

    QByteArray response_data=reply->readAll();
     // Muunnetaan vastaus QByteArray-tyyppisestä JSON-dokumentiksi
    QJsonDocument json_doc = QJsonDocument::fromJson(response_data);
    // Muunnetaan JSON-dokumentti JSON-arrayksi
    QJsonArray json_array = json_doc.array();
    // Käsitellään JSON-arraytä

    reply->deleteLater();
    manager->deleteLater();
}    
deleteLater
deleteLater()-metodia käytetään yleensä Qt-sovelluksissa tilanteissa, joissa halutaan poistaa dynaamisesti luotu olio turvallisesti. Tämä metodi asettaa olion poistettavaksi myöhemmin Qt:n tapahtumakäsittelyjärjestelmän kautta, mikä varmistaa, että olio poistetaan oikeassa kohtaa ohjelman suoritusjärjestystä. Tämä on erityisen tärkeää, jos olio on sidottu muihin osiin ohjelmaa, kuten signaaleihin.
fromJson-metodi

fromJson metodia, voidaan kutsua myös antamalla QJsonParseError-viite seuraavasti:

QJsonParseError parseError;
QJsonDocument json_doc = QJsonDocument::fromJson(response_data, &parseError);
Silloin voidaan tarkistaa tapahtuiko virhe seuraavasti:
if (parseError.error != QJsonParseError::NoError) {
    qWarning() << "JSON parsing error:" << parseError.errorString();
    // Tässä vaiheessa json_doc ei ole kelvollinen
}

JSON-objektin kenttiin päästään käsiksi seuraavasti:

int book_id = json_obj["id_book"].toInt();
QString book_name = json_obj["name"].toString();

JSON-array:tä voidaan käsitellä seuraavalla foreach-loopilla:

foreach (const QJsonValue &value, json_array) {
    // Toimenpiteet
}

Jos haluat päästä käsiksi JSON-arrayn yksittäisen objektin kenttään, voit käyttää seuraavaa rakennetta

QString book_name = json_array.at(0)["name"].toString();
Edellä QStringiin nimeltään book_name sijoitetaan ensimmäisen objektin (at(0)) name kentän arvo.

JSON-datan käsittely C++-olioiden avulla

Käytännössä JSON-data kannattaa usein mäpätä C++-olioiksi, jolloin sitä on helpompi käsitellä. Esimerkiksi edellisten esimerkkien tapauksessa voisi luoda Book-luokan seuraavilla koodeilla:

#ifndef BOOK_H
#define BOOK_H

#include <qjsonobject.h>

class Book
{
public:
    int id;
    QString name;
    QString author;

    static Book mapJson(const QJsonObject &json);
};

#endif // BOOK_H
#include "book.h"

Book Book::mapJson(const QJsonObject &json) {
    Book book;
    book.id = json["id_book"].toInt();
    book.name = json["name"].toString();
    book.author = json["author"].toString();
    return book;
}

Jos http-responsena saadaan JSON-objekti voidaan luoda Book-luokan olio seuraavasti:

Book book = Book::mapJson(json_obj);

Jos http-responsena saadaan json array voidaan luoda Book-olioita sisältävä QVector seuraavasti:

QVector<Book> bookList;

    for (const QJsonValue &value : json_array) {
        if (value.isObject()) {
            Book book = Book::mapJson(value.toObject());
            bookList.append(book);
        }
    }

Edellä metodi mapJson määriteltiin staattiseksi, jotta:

  1. Se ei tarvitse olemassaolevaa oliota toimiakseen
  2. Sen on tarkoitus luoda uusi Book-olio annetusta json-objektista
Kyseessä on siis ns. factory-metodi.