簡體   English   中英

如何使QNetworkReply返回自定義數據?

[英]How to make QNetworkReply to return custom data?

我將使用QNetworkAccessManager通過移動應用程序向服務器發出對HTTP服務器的請求。 問題是,如何將自定義數據鏈接到每個請求? 我嘗試對QNetworkReply進行子類化,但發現必須實現虛方法close()isSequential()但我不知道它們應該返回什么,因此恐怕會破壞網絡請求功能。

例如,當我的應用程序執行登錄過程時,它必須存儲該帳戶的電子郵件地址:

class MyApp : public QObject
{
    Q_OBJECT

private:
    QNetworkRequest             request;
    QNetworkReply               *reply;
    QNetworkAccessManager       *manager;

    ...

}

void MyApp::do_log_in(QString email, QString password) {
    QString s;

    someobject.email=email; // <-- I have to store email address before sending request to server, but where do I store it?
    s.append("http://myapp.com/do-auth.php?email=");
    s.append(QUrl::toPercentEncoding(email));
    s.append("&password=");
    s.append(QUrl::toPercentEncoding(password));
    connect(manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(login_finished(QNetworkReply*)));
    request.setUrl(QUrl(s));
    manager->get(request);

}

void MyApp::login_finished(QNetworkReply *rep) {
    DepservReply *reply;
    QString email;
    ....
    email= ...... // <-- I need to get the email address from QNetworkReply object somehow
    ///my code here handling server reply
    ....
} 

那么,在我的情況下,如何實現電子郵件的存儲和檢索,我應該子類化什么類,我應該重新實現什么方法?

您可以利用每個QObject可用的動態屬性系統,並將數據保留在答復中:

// https://github.com/KubaO/stackoverflown/tree/master/questions/network-reply-tracking-40707025
#include <QtNetwork>

class MyCtl : public QObject
{
    Q_OBJECT
    QNetworkAccessManager manager{this};
    // ...
    void reply_finished(QNetworkReply *reply);
public:
    MyCtl(QObject *parent = nullptr);
    void do_log_in(const QString &email, const QString &password);
};

static const char kAuthGetSalt[] = "req_auth-get-salt";
static const char kDoAuth[] = "req_do-auth";
static const char kEmail[] = "req_email";
static const char kPassword[] = "req_password";

static const auto authGetSaltUrl = QStringLiteral("https://myapp.com/auth-get-salt.php?email=%1");
static const auto doAuthUrl = QStringLiteral("https://myapp.com/do-auth.php?email=%1&passwordHash=%2");

MyCtl::MyCtl(QObject *parent) : QObject{parent}
{
    connect(&manager, &QNetworkAccessManager::finished, this, &MyCtl::reply_finished);
}

void MyCtl::do_log_in(const QString &email, const QString &password) {
    auto url = authGetSaltUrl.arg(email);
    auto reply = manager.get(QNetworkRequest{url});
    reply->setProperty(kAuthGetSalt, true);
    reply->setProperty(kEmail, email);
    reply->setProperty(kPassword, password);
}

void MyCtl::reply_finished(QNetworkReply *reply) {
    if (!reply->property(kAuthGetSalt).isNull()) {
        reply->deleteLater(); // let's not leak the reply
        if (reply->error() == QNetworkReply::NoError) {
            auto salt = reply->readAll();
            auto email = reply->property(kEmail).toString();
            auto password = reply->property(kPassword).toString();
            Q_ASSERT(!password.isEmpty() && !email.isEmpty());
            QCryptographicHash hasher{QCryptographicHash::Sha1};
            hasher.addData(salt); // the server must hash the same way
            hasher.addData("----");
            hasher.addData(password.toUtf8());
            auto hash = hasher.result().toBase64(QByteArray::Base64UrlEncoding);
            auto url = doAuthUrl.arg(email).arg(QString::fromLatin1(hash));

            auto reply = manager.get(QNetworkRequest{url});
            reply->setProperty(kDoAuth, true);
            reply->setProperty(kEmail, email);
        }
    }
    else if (!reply->property(kDoAuth).isNull()) {
        if (reply->error() == QNetworkReply::NoError) {
            auto email = reply->property(kEmail).toString();
            // ...
        }
    }
}

通過讓編譯器檢查您使用的是有效標識符,對屬性名稱使用常量以避免輸入錯誤。

上面的示例糾正了代碼中的以下關鍵安全問題:

  1. 通過清晰的連接發送安全憑證:使用https://而不是http://

  2. 以明文形式發送密碼:相反,發送密碼的哈希值。 創建帳戶時,服務器應為每個帳戶隨機生成一個鹽。 現有帳戶可以不加鹽,但是一旦用戶更改密碼,它們應立即注冊。

還要注意,從QStringQUrl轉換將自動對字符串進行百分比編碼,因此無需顯式地執行此操作。

在這種情況下, email是請求URL的一部分,因此您可以從那里提取emailQNetworkReply有權訪問它正在處理的QNetworkRequest ,請參閱QNetworkReply::request() )。

由於QNetworkReplyQObject派生的類,因此您還可以或多或少地存儲任何類型的數據作為動態屬性,請參見QObject::setProperty()

您可以繼承QNAM以獲得更多控制權。

網絡

class QNAMNetwork : public QNetworkAccessManager
{
    Q_OBJECT
public:
    explicit QNAMNetwork(QObject *parent = 0);
    ~QNAMNetwork();
    inline void insertUserValue(const QString & key, const QString & value){this->m_user_values.insert(key,value);}
    inline QString getUserValue(const QString & key){return this->m_user_values.value(key);}


signals:
    void requestFinished(ExNetwork *, QNetworkReply *);

private slots:
    void _sslErrors(QNetworkReply *, const QList<QSslError> &);
    void _finished(QNetworkReply *);

private:
    QMap<QString, QString>           m_user_values;
 };

network.cpp

QNAMNetwork::QNAMNetwork(QObject *parent):QNetworkAccessManager(parent)
{
    connect(this, &QNAMNetwork::sslErrors, this, &QNAMNetwork::_sslErrors);
    connect(this, &QNAMNetwork::finished, this, &QNAMNetwork::_finished);
}

QNAMNetwork::~QNAMNetwork()
{
    //qDebug() << __FUNCTION__ << QString::number((long)this,16);
}

void QNAMNetwork::_sslErrors(QNetworkReply * reply, const QList<QSslError> & errors)
{
    reply->ignoreSslErrors(errors);
}

void QNAMNetwork::_finished(QNetworkReply * reply)
{
    emit requestFinished(this, reply);
}

用例:

QNAMNetwork * network = new QNAMNetwork(this);
network->insertUserValue("email","yourmail@mail.com");
connect(network, SIGNAL(requestFinished(QNAMNetwork*,QNetworkReply*)), this, SLOT(requestFinished(QNAMNetwork*,QNetworkReply*)));

QNetworkRequest req(QUrl::fromUserInput(query)); //get url

network->get(req);

...

void YourClass::requestFinished(QNAMNetwork * net, QNetworkReply * rep)
{
   QString email = net->getUserValue("email");
   net->deleteLater();
   rep->deleteLater();
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM