客户端网络接口访问方式

       在最近的一个客户端软件产品中,需要用到大量的网络服务器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"));
}

 

记下来备用,待实际中使用时再进一步完善优化,包括存在的问题和代码结构。

你可能感兴趣的:(项目实战)