我将使用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
....
}
那么,在我的案例中,如何实现电子邮件的存储和检索,我应该将哪些类子类化以及应该重新实现哪些方法?
答案 0 :(得分:3)
您可以利用每个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();
// ...
}
}
}
使用常量作为属性名称,以避免打字错误,让编译器检查您是否使用了有效的标识符。
上述示例纠正了代码中的以下严重安全问题:
通过明确的连接发送安全凭据:使用https://
,而不是http://
。
以明文形式发送密码:改为发送盐渍哈希值。创建帐户时,您的服务器应为每个帐户生成随机盐。现有帐户可以保持不变,但是一旦用户更改密码,他们就应该获得一个盐。
另请注意,QString
到QUrl
转换会自动对字符串进行百分比编码,因此无需明确地执行此操作。
答案 1 :(得分:2)
在这种情况下,email
是请求网址的一部分,因此您可以从那里提取它(QNetworkReply
可以访问它正在处理的QNetworkRequest
,请参阅QNetworkReply::request()
)。
您还可以将或多或少的任何类型的数据存储为动态属性,因为QNetworkReply
是QObject
派生类,请参阅QObject::setProperty()
。
答案 2 :(得分:0)
您可以将QNAM子类化以获得对它的更多控制。
network.h
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();
}