Network access
The low-level networking protocol negotiation is all handled internally by Qt, and we can easily get connected to the outside world via the QNetworkAccessManager class. To be able to access this functionality, we need to add the network module to cm-lib.pro:
QT += sql network
One of Qt's weaknesses is the lack of interfaces, making unit testing difficult in some cases. If we just use QNetworkAccessManager directly, we won’t be able to test our code without making real calls to the network, which is undesirable. However, a quick and easy solution to this problem is to hide the Qt implementation behind an interface of our own, and we will do that here.
For the purposes of this chapter, all we need to be able to do with the network is check that we have connectivity and send a HTTP GET request. With this in mind, create a header file i-network-access-manager.h in a new folder cm-lib/source/networking and implement the interface:
#ifndef INETWORKACCESSMANAGER_H #define INETWORKACCESSMANAGER_H
#include <QNetworkReply> #include <QNetworkRequest> namespace cm { namespace networking {
class INetworkAccessManager { public: INetworkAccessManager(){} virtual ~INetworkAccessManager(){}
virtual QNetworkReply* get(const QNetworkRequest& request) = 0; virtual bool isNetworkAccessible() const = 0; };
}}
#endif
QNetworkRequest is another Qt class that represents a request to be sent over the network, and QNetworkReply represents a response received over the network. We will ideally hide these implementations behind interfaces too, but let’s make do with the network access interface for now. With that in place, go ahead and create a concrete implementation class NetworkAccessManager in the same folder:
network-access-manager.h:
#ifndef NETWORKACCESSMANAGER_H #define NETWORKACCESSMANAGER_H
#include <QObject> #include <QScopedPointer> #include <networking/i-network-access-manager.h>
namespace cm { namespace networking {
class NetworkAccessManager : public QObject, public INetworkAccessManager { Q_OBJECT
public: explicit NetworkAccessManager(QObject* parent = nullptr); ~NetworkAccessManager();
QNetworkReply* get(const QNetworkRequest& request) override; bool isNetworkAccessible() const override;
private: class Implementation; QScopedPointer<Implementation> implementation; };
}}
#endif
network-access-manager.cpp:
#include "network-access-manager.h" #include <QNetworkAccessManager>
namespace cm { namespace networking {
class NetworkAccessManager::Implementation { public: Implementation() {} QNetworkAccessManager networkAccessManager; };
NetworkAccessManager::NetworkAccessManager(QObject *parent) : QObject(parent) , INetworkAccessManager() { implementation.reset(new Implementation()); }
NetworkAccessManager::~NetworkAccessManager() { }
QNetworkReply* NetworkAccessManager::get(const QNetworkRequest& request) { return implementation->networkAccessManager.get(request); }
bool NetworkAccessManager::isNetworkAccessible() const { return implementation->networkAccessManager.networkAccessible() == QNetworkAccessManager::Accessible; }
}}
All we are doing is holding a private instance of QNetworkAccessManager and passing calls to our interface through to it. The interface can easily be extended to include additional functionality like HTTP POST requests with the same approach.