在最近的一个客户端软件产品中,需要用到大量的网络服务器API接口调用,每调用一个网络接口,我们都会增加一个类来对这个接口进行封装,接口一旦多了,类数量迅速膨胀,其实每个类里面都大同小异,只是部分代码不同而已,
如果说少的话,还好!但我总觉得不爽,觉得这不是使用网络接口的正确调用方式,在有些客户端产品中,对网络接口的调用可能多达几百甚至上千个,如果每个接口都对应一个类,那还得了!因为我一直在琢磨一个可以缓解类数量膨胀的方式,不知道是否会有其他的问题,这里记录下来备用。只是写了个大概思路。
1、核心就是一个类: QtNetwokApiAccessor 网络接口访问类。
每个接口的调用方自己组装好请求头(包括Url)和请求体(数据)传入,并主动接收QtNetwokApiAccessor 对象的完成信号以获取返回的结果数据(QJsonObject)或失败的相关信息。
2、提供了3个静态工具函数: isHttps、 sslVerify 和 setRawHeaderForRequest, 以方便每个调用方组装数据时根据需要调用,达到消除重复代码的目的。
3、数据组装示例代码:
1)get
QNetworkRequest TNRequestDataMaker::makeGGGRequest()
{
QString baseUrl("api-url-is-fixed");
QUrl url(baseUrl);
QUrlQuery query;
//query.addQueryItem("comId", comId);
url.setQuery(query);
QNetworkRequest httpRequest;
setRawHeaderForRequest(httpRequest);
httpRequest.setUrl(url);
if (isHttps(baseUrl))
sslVerify(httpRequest);
return httpRequest;
}
2)post
QPair TNRequestDataMaker::makePPPRequest()
{
QString baseUrl("http://udapi.systoontest.com/integration/integration/getConfigTemplate");
QUrl url(baseUrl);
QJsonObject jsonObject;
jsonObject.insert("platform", 3);
QJsonDocument document;
document.setObject(jsonObject);
QByteArray dataArray = document.toJson(QJsonDocument::Compact);
QNetworkRequest httpRequest;
setRawHeaderForRequest(httpRequest);
httpRequest.setUrl(url);
if (isHttps(baseUrl))
sslVerify(httpRequest);
return QPair(httpRequest, dataArray);
}
3) QtNetwokApiAccessor 类
#ifndef QtNetwokApiAccessor_H
#define QtNetwokApiAccessor_H
#include
#include
#include
class QSslError;
class QNetworkReply;
class QNetworkRequest;
class QNetworkAccessManager;
class QtNetwokApiAccessor : public QObject
{
Q_OBJECT
public:
explicit QtNetwokApiAccessor(QObject *parent=Q_NULLPTR);
~QtNetwokApiAccessor();
QNetworkReply* get(const QNetworkRequest &request, const QVariant &userData=QVariant());
QNetworkReply* post(const QNetworkRequest &request, const QByteArray &data, const QVariant &userData=QVariant());
signals:
void signalRequestFinished(const QJsonObject &jsonObject, const QVariant &userData);
private slots:
void slotRequestFinished(QNetworkReply *reply);
void slotHandleSslErrors(QNetworkReply *reply, const QList &errors);
void slotHandleRepyError(QNetworkReply::NetworkError code);
private:
static QNetworkAccessManager *m_networkAccessManager;
QMap m_userDataMap;
public:
static bool isHttps(const QString &url);
static void sslVerify(QNetworkRequest &request);
// 设置请求头,对不同的服务/域可能需要设置不同请求头,因此可以定义不同的设置请求头的函数
static void setRawHeaderForRequest(QNetworkRequest &httpRequest/*, const TNPluginParamPtr &pluginParam*/);
};
#endif // QtNetwokApiAccessor_H
实现文件:
#include "QtNetwokApiAccessor.h"
#include
#include
#include
#include
#include
#include
#include
#include
QNetworkAccessManager* QtNetwokApiAccessor::m_networkAccessManager = nullptr;
QtNetwokApiAccessor::QtNetwokApiAccessor(QObject *parent) : QObject(parent)
{
if (m_networkAccessManager == nullptr)
{
m_networkAccessManager = new QNetworkAccessManager(QCoreApplication::instance());
QNetworkConfigurationManager manager;
m_networkAccessManager->setConfiguration(manager.defaultConfiguration());
m_networkAccessManager->setNetworkAccessible(QNetworkAccessManager::Accessible);
connect(m_networkAccessManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(slotRequestFinished(QNetworkReply*)));
connect(m_networkAccessManager, SIGNAL(sslErrors(QNetworkReply*, const QList&)), this, SLOT(slotRequestFinished(QNetworkReply*)));
}
}
QtNetwokApiAccessor::~QtNetwokApiAccessor()
{
}
QNetworkReply* QtNetwokApiAccessor::get(const QNetworkRequest &request, const QVariant &userData)
{
m_networkAccessManager->setNetworkAccessible(QNetworkAccessManager::Accessible);
QNetworkReply *reply = m_networkAccessManager->get(request);
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(slotHandleRepyError(QNetworkReply::NetworkError)));
m_userDataMap[reply] = userData;
return reply;
}
QNetworkReply* QtNetwokApiAccessor::post(const QNetworkRequest &request, const QByteArray &data, const QVariant &userData)
{
m_networkAccessManager->setNetworkAccessible(QNetworkAccessManager::Accessible);
QNetworkReply *reply = m_networkAccessManager->post(request, data);
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(slotHandleRepyError(QNetworkReply::NetworkError)));
m_userDataMap[reply] = userData;
return reply;
}
void QtNetwokApiAccessor::slotRequestFinished(QNetworkReply *reply)
{
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
const QByteArray &resultData = reply->readAll(); // 这里使用引用是考虑到数据量大而且调用频繁情况下的效率
qInfo() << "QtNetwokApiAccessor::slotRequestFinished statusCode=" << statusCode << " url=" << reply->url();
qDebug() << "QtNetwokApiAccessor::slotRequestFinished resultData=" << resultData;
QJsonObject resJsonObject;
if (reply->error() == QNetworkReply::NoError)
{
if (statusCode == 200)
{
QJsonParseError jsonError;
resJsonObject = QJsonDocument::fromJson(resultData, &jsonError).object();
if (jsonError.error != QJsonParseError::NoError)
{
qCritical() << "QtNetwokApiAccessor::slotRequestFinished-JsonParseError errorCode=" << jsonError.error << " errorMsg=" << jsonError.errorString();
}
}
}
else
{
qCritical() << "QtNetwokApiAccessor::slotRequestFinished-NetworkError errorCode=" << reply->error() << " errorMsg=" << reply->errorString();
}
// 保证一定会有信号发出来
emit signalRequestFinished(resJsonObject, m_userDataMap.take(reply));
// At the end of that slot, we won't need it anymore
reply->deleteLater();
}
void QtNetwokApiAccessor::slotHandleSslErrors(QNetworkReply *reply, const QList &errors)
{
QString errorString;
foreach(const QSslError &error, errors)
{
if (!errorString.isEmpty())
errorString += '\n';
errorString += error.errorString();
}
qCritical() << "QtNetwokApiAccessor::slotHandleSslErrors errorString=" << errorString;
reply->ignoreSslErrors();
}
void QtNetwokApiAccessor::slotHandleRepyError(QNetworkReply::NetworkError code)
{
qCritical() << "QtNetwokApiAccessor::slotHandleRepyError errorCode=" << code;
QNetworkReply *reply = qobject_cast(this->sender());
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
qWarning() << "QtNetwokApiAccessor::slotHandleRepyError statusCode=" << statusCode << " url=" << reply->url();
qCritical() << "QtNetwokApiAccessor::slotHandleRepyError errorCode=" << reply->error() << " errorMsg=" << reply->errorString();
// 保证一定会有信号发出来
emit signalRequestFinished(QJsonObject(), m_userDataMap.take(reply));
// At the end of that slot, we won't need it anymore
reply->deleteLater();
}
bool QtNetwokApiAccessor::isHttps(const QString &url)
{
return url.startsWith("https:");
}
void QtNetwokApiAccessor::sslVerify(QNetworkRequest& request)
{
// 发送https请求前准备工作;
QSslConfiguration conf = request.sslConfiguration();
conf.setPeerVerifyMode(QSslSocket::VerifyNone);
conf.setProtocol(QSsl::TlsV1SslV3);
request.setSslConfiguration(conf);
}
void QtNetwokApiAccessor::setRawHeaderForRequest(QNetworkRequest &httpRequest/*, const TNPluginParamPtr &pluginParam*/)
{
// httpRequest.setRawHeader(QByteArray("deviceId"), pluginParam->strMacAddress.toUtf8());
// httpRequest.setRawHeader(QByteArray("platform"), pluginParam->platform.toUtf8());
// httpRequest.setRawHeader(QByteArray("platformVersion"), pluginParam->platformVersion.toUtf8());
// httpRequest.setRawHeader(QByteArray("appVersion"), pluginParam->appVersion.toUtf8());
// httpRequest.setRawHeader(QByteArray("toonType"), pluginParam->toonType.toUtf8());
// httpRequest.setRawHeader(QByteArray("Content-Type"), QByteArray("application/json"));
}
记下来备用,待实际中使用时再进一步完善优化,包括存在的问题和代码结构。