繁体   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