RESTful Client Applications in Qt 6.7 and Forward

Qt 6.7 introduces convenience improvements for implementing typical RESTful/HTTP client applications. The goal was/is to reduce the repeating networking boilerplate code by up to 40% by addressing the small but systematically repeating needs in a more convenient way. 

These include a new QHttpHeaders class for representing HTTP headers, QNetworkRequestFactory for creating API-specific requests,  QRestAccessManager class for addressing small but often-repeating pieces of code, and QRestReply class for extracting the data from replies and checking for errors. QNetworkRequestFactory, QRestAccessManager, and QRestReply are released as Technical Previews in Qt 6.7.

Below is a screenshot of the Qt Colorpalette Client example application, which uses these new enablers. The client application interfaces a REST API and visualizes the 'color' and 'user' resources.

colorpaletteclient

REST Briefly

REST is an architectural style used by most of the networked applications we use today. ‘REST’ is not a strict technical specification but rather a set of constraints and recommendations on organizing and accessing resources. Technically, it boils down to sending and serving HTTP requests, making it somewhat common to talk about HTTP clients and RESTful clients interchangeably.  

Since Qt 4.4 (2008) the main class for developing RESTful/HTTP client applications on the C++ side is the QNetworkAccessManager. With it, you can issue HTTP requests and use signal-slot connections to handle the responses.

On the QML and Javascript side, there’s an XmlHttpRequests class for a similar purpose. More complex QML applications also use the C++ QNetworkAccessManager and bridge/expose the networking to QML as data and control types/elements.

New in Qt 6.7: QHttpHeaders


HTTP headers in Qt are represented by a QMap, QHash, list of pairs, QMultiMap, or QMultiHash. This works, but the exact type is API-specific and doesn’t allow for common header convenience operations, performance optimizations, or, for example, checking that header names and values are valid according to RFC specifications.

Qt 6.7 introduces QHttpHeaders class to represent HTTP headers. For now, the class is mostly used internally. Later Qt releases will expand the public networking APIs to support this class.

An example of an internal performance optimization that this enables is that we can internally use enums instead of strings to represent any of the 100+ well-known headers. This makes lookups and storage computationally more efficient.

New in Qt 6.7: QNetworkRequestFactory


Most REST server APIs require information that repeats from request to request: the URL, query parameters, SSL config, and authentication details. These requests also have parts that typically change from request to request: the resource path and query parameters. 

QNetworkRequestFactory allows you to define the common parameters only once and, after that, pass only request-specific parameters when creating new requests. The created requests are then passed on to QNetworkAccessManager or QRestAccessManager. 

A typical use would be to have a factory for each API of interest:

// Instantiate a network access manager somewhere in the application
QNetworkAccessManager manager;
// … initialize manager

// Instantiate a factory somewhere suitable in the application
QNetworkRequestFactory exampleApi{ {"https://example.com/v1"_L1} };
// Set bearer token
exampleApi.setBearerToken("my_token");
// Fill common headers
QHttpHeaders commonHeaders;
exampleApi.setCommonHeaders(commonHeaders);
// … set query parameters, etc …
// Create and issue request to “https://example.com/v1/models”
manager.get(exampleApi.createRequest("models"_L1));

The QNetworkRequestFactory class will be released in Qt 6.7 as a Technical Preview, and the API has already been further amended for Qt 6.8, thanks to the early user feedback. 

New Qt 6.7: QRestAccessManager and QRestReply

QRestAccessManager and related QRestReply are thin, non-owning, convenience wrappers on top of QNetworkAccessManager and QNetworkReply. They streamline the typical RESTful/HTTP client needs: 

  • Accept JSON and other types directly as request data
  • Provide JSON and other types (notably text) on the reply
  • Error handling (distinguish between network errors and HTTP errors)
  • Accept contextful callbacks directly when issuing the request
  • And others, including named API for sending patch() requests

While these improvements aren’t necessarily very large on their own, they reduce the required boilerplate and head-scratching.  Below is an example of posting and receiving JSON. It reduces the needed code in several ways, enhancing convenience:

  • Send the JSON data directly (‘myJson’). There is no need to convert manually or set ‘Content-Type:application/json’ header.
  • Define the callback directly (‘this’ is used as context object to clean up if ‘this’ is deleted)
  • Read the response JSON directly (‘readJson()’).  There is no need to write the conversion from a QByteArray. The JSON parsing error is also available if needed. This example skips success checks, but it could be done with ‘reply.isSuccess()’ 
manager->post(request, myJson, this, [this](QRestReply &reply) {
    if (auto json = reply.readJson()) {
        // use *json
    }
});

If the expected data were text, it could’ve been read as it arrives (streaming); QRestReply implements streaming text decoding and uses the indicated ‘charset’ parameter (e.g., UTF-8).

Note: since these are mere non-owning convenience wrappers, you can still use your pre-existing QNetworkAccessManager code and, for instance, wrap the received QNetworkReply* in a QRestReply.

QRestAccessManager and QRestReply are released as Technical Previews in Qt 6.7.

Future


There are many more related items in the planning stage, and feedback for them is welcome:

QHttpMultipart convenience API for form-data
QtNetworkAuthorization module improvements
QML/Javascript Fetch()
OpenAPI client generator support
OpenAPI server generator support
General ideation and feedback


Blog Topics:

Comments