我正在为我的个人培训编写一个简单的Web服务客户端。我决定用Qt来做。我使用的是 5.7 版。我设法克服了从服务器发送和接收请求的麻烦。但仍然存在一个问题。
我有一个插槽on_scanButton_click()
,根据选中的单选按钮,它调用对服务器的一些请求。我的问题是如何在第一个带有句点的请求之后向服务器发送另一个请求,以便我可以检查response_code是否有效,并且可以为其调用单独的窗口。
这是我的主窗口.cpp
#include "mainwindow.h"
#include "networkmanager.h"
#include "commentdialog.h"
#include "rescanfiledialog.h"
#include "filebrowser.h"
#include "program_exceptions.h"
#include "overlaywidget.h"
#include <QMessageBox>
#include <regex>
MainWindow::MainWindow(QWidget* parent) :
QMainWindow{parent} {
ui.setupUi(this);
ui.fileRadioButton->setFixedWidth(100);
ui.urlRadioButton->setFixedWidth(100);
ui.searchRadioButton->setFixedWidth(100);
ui.stringFormater->setFixedSize(500, 30);
ui.stringFormater->setPlaceholderText("No file selected");
ui.uploadButton->setFixedHeight(30);
ui.scanButton->setFixedSize(120, 50);
ui.rescanButton->setFixedSize(120, 50);
ui.commentButton->setFixedSize(120, 50);
}
void MainWindow::changeEvent(QEvent* e) {
QMainWindow::changeEvent(e);
switch (e->type()) {
case QEvent::LanguageChange:
ui.retranslateUi(this);
break;
default:
break;
}
}
void MainWindow::on_scanButton_clicked() {
NetworkManager* network_manager{new NetworkManager};
QString input_string = ui.stringFormater->text();
try {
if (ui.fileRadioButton->isChecked()) {
if (std::regex_match(input_string.toStdString(), std::regex("^(.*/)([^/]*)$"))) {
network_manager->scanFileRequest(input_string);
} else {
throw InvalidFilePathException();
}
} else if (ui.urlRadioButton->isChecked()) {
if (std::regex_match(input_string.toStdString(),
std::regex("^(ht{2}ps?:\/{2})?(w{3}\.)?([^:\/\.\s]+)\.([^:\/\.\s]+)$"))) {
network_manager->scanUrlRequest(input_string);
} else {
throw InvalidUrlNameException();
}
} else if (ui.searchRadioButton->isChecked()) {
if (!std::regex_match(input_string.toStdString(), std::regex("([^:\/\.\s]+)\.([^:\/\.\s]+)$"))) {
if (!std::regex_match(input_string.toStdString(), std::regex("^(([0-9]{1,3})\.){3}([0-9]{1,3})$"))) {
throw InvalidIpAddressException();
} else {
network_manager->retrieveIpReportRequest(input_string);
}
throw InvalidDomainNameException();
} else {
network_manager->retrieveDomainReportRequest(input_string);
}
}
} catch (std::exception& ex) {
QString exception_message{ex.what()};
QMessageBox::warning(this, "Warning", exception_message);
}
ui.stringFormater->clear();
}
void MainWindow::on_fileRadioButton_clicked() {
ui.stringFormater->setPlaceholderText("No file selected");
ui.uploadButton->setText("Choose File");
ui.scanButton->setText("Scan it!");
ui.stringFormater->clear();
}
void MainWindow::on_urlRadioButton_clicked() {
ui.stringFormater->setPlaceholderText("http://example.com");
ui.uploadButton->setText("Enter URL");
ui.scanButton->setText("Scan it!");
ui.stringFormater->clear();
}
void MainWindow::on_searchRadioButton_clicked() {
ui.stringFormater->setPlaceholderText("hash, URL, domain, IP address...");
ui.uploadButton->setText("Enter Term");
ui.scanButton->setText("Search it!");
ui.stringFormater->clear();
}
void MainWindow::on_uploadButton_clicked() {
if (ui.fileRadioButton->isChecked()) {
FileBrowser* file_browser{new FileBrowser(this)};
file_browser->exec();
ui.stringFormater->setText(file_browser->getFilePath());
file_browser->setFilePath("");
}
}
void MainWindow::on_rescanButton_clicked() {
RescanFileDialog* rescan_file_doalog{new RescanFileDialog(this)};
rescan_file_doalog->exec();
}
void MainWindow::on_commentButton_clicked() {
CommentDialog* comment_dialog{new CommentDialog(this)};
comment_dialog->exec();
}
这是网络管理器。
#ifndef NETWORKMANAGER_H
#define NETWORKMANAGER_H
#include <QtNetwork>
class NetworkManager : public QObject {
Q_OBJECT
private:
QString api_address{"https://www.virustotal.com/vtapi/v2"};
QByteArray api_key{"API KEY HERE"};
QNetworkAccessManager* network_manager{new QNetworkAccessManager(this)};
static QJsonObject json_response;
public:
explicit NetworkManager() {
connect(network_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(requestFinished(QNetworkReply*)));
}
virtual ~NetworkManager() = default;
void scanFileRequest(const QString&);
void rescanFileRequest(const QString&);
void retrieveFileReportRequest(const QString&);
void scanUrlRequest(const QString&);
void retrieveUrlReportRequest(const QString&);
void retrieveIpReportRequest(const QString&);
void retrieveDomainReportRequest(const QString&);
void makeCommentRequest(const QString&, const QString&);
const static QJsonObject getJsonResponse() {
return json_response;
}
private slots:
void requestFinished(QNetworkReply*);
};
#endif // NETWORKMANAGER_H
和网络管理器.cpp。
#include "networkmanager.h"
#include "responses.h"
#include "status_codes.h"
#include "program_exceptions.h"
#include <QMessageBox>
#include <QTimer>
QJsonObject NetworkManager::json_response{};
void NetworkManager::scanFileRequest(const QString& absolute_file_path) {
const QFileInfo file_info{absolute_file_path};
QHttpMultiPart* multi_part{new QHttpMultiPart(QHttpMultiPart::FormDataType)};
QHttpPart api_key_part{};
api_key_part.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name="apikey""));
api_key_part.setBody(api_key);
QHttpPart file_part{};
QMimeDatabase db{};
QMimeType mime_message{db.mimeTypeForFile(file_info)};
file_part.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(mime_message.name()));
file_part.setHeader(QNetworkRequest::ContentDispositionHeader,
QVariant("form-data; name="file"; filename="" + file_info.fileName() + """));
QFile* file{new QFile(absolute_file_path)};
try {
if (!file->open(QIODevice::ReadOnly)) {
throw FileDoesNotExistException();
}
} catch (std::exception& ex) {
QMessageBox message_box{QMessageBox::Warning, "Warning", QObject::tr(ex.what()),
QMessageBox::NoButton, 0, Qt::FramelessWindowHint};
message_box.exec();
}
file_part.setBodyDevice(file);
file->setParent(multi_part);
multi_part->append(api_key_part);
multi_part->append(file_part);
QNetworkRequest request{QUrl{api_address + "/file/scan"}};
network_manager->post(request, multi_part);
}
void NetworkManager::rescanFileRequest(const QString& resource) {
QUrlQuery query_set{};
query_set.addQueryItem("apikey", api_key);
query_set.addQueryItem("resource", resource);
QUrl post_params{};
post_params.setQuery(query_set);
QByteArray post_data{post_params.toEncoded(QUrl::RemoveFragment)};
post_data.remove(0, 1);
QNetworkRequest request{QUrl{api_address + "/file/rescan"}};
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
network_manager->post(request, post_data);
}
void NetworkManager::retrieveFileReportRequest(const QString& resource) {
QUrl url{api_address + "/file/report"};
QUrlQuery query_set{};
query_set.addQueryItem("apikey", api_key);
query_set.addQueryItem("resource", resource);
url.setQuery(query_set.query());
QNetworkRequest request{url};
network_manager->get(request);
}
void NetworkManager::scanUrlRequest(const QString& url) {
QUrlQuery query_set{};
query_set.addQueryItem("apikey", api_key);
query_set.addQueryItem("url", url);
QUrl post_params{};
post_params.setQuery(query_set);
QByteArray post_data{post_params.toEncoded(QUrl::RemoveFragment)};
post_data.remove(0, 1);
QNetworkRequest request{QUrl{api_address + "/url/scan"}};
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
network_manager->post(request, post_data);
}
void NetworkManager::retrieveUrlReportRequest(const QString& resource) {
QUrlQuery query_set{};
query_set.addQueryItem("apikey", api_key);
query_set.addQueryItem("resource", resource);
QUrl post_params{};
post_params.setQuery(query_set);
QByteArray post_data{post_params.toEncoded(QUrl::RemoveFragment)};
post_data.remove(0, 1);
QNetworkRequest request{QUrl{api_address + "/url/report"}};
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
network_manager->post(request, post_data);
}
void NetworkManager::retrieveIpReportRequest(const QString& ip) {
QUrl url{api_address + "/ip-address/report"};
QUrlQuery query_set{};
query_set.addQueryItem("apikey", api_key);
query_set.addQueryItem("ip", ip);
url.setQuery(query_set.query());
QNetworkRequest request{url};
network_manager->get(request);
}
void NetworkManager::retrieveDomainReportRequest(const QString& domain) {
QUrl url{api_address + "/domain/report"};
QUrlQuery query_set{};
query_set.addQueryItem("apikey", api_key);
query_set.addQueryItem("domain", domain);
url.setQuery(query_set.query());
QNetworkRequest request{url};
network_manager->get(request);
}
void NetworkManager::makeCommentRequest(const QString& resource, const QString& comment) {
QUrlQuery query_set{};
query_set.addQueryItem("apikey", api_key);
query_set.addQueryItem("resource", resource);
query_set.addQueryItem("comment", comment);
QUrl post_params{};
post_params.setQuery(query_set);
QByteArray post_data{post_params.toEncoded(QUrl::RemoveFragment)};
post_data.remove(0, 1);
QNetworkRequest request{QUrl{api_address + "/comments/put"}};
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
network_manager->post(request, post_data);
}
void NetworkManager::requestFinished(QNetworkReply* reply) {
try {
http_status_code_t server_reply{static_cast<http_status_code_t>(
reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt())};
if (server_reply == OK) {
QJsonObject json_object = QJsonDocument::fromJson(reply->readAll()).object();
response_code_t response_code{static_cast<response_code_t>(json_object["response_code"].toInt())};
std::string verbose_msg{json_object["verbose_msg"].toString().toStdString()};
if (response_code == ITEM_IS_PRESENT) {
json_response = json_object;
qDebug() << json_response;
} else if (response_code == ITEM_IS_STILL_IN_QUEUE) {
throw RequestStillInQueueException(verbose_msg);
} else if (response_code == ITEM_DOES_NOT_EXIST) {
throw ItemDoesNotExistException(verbose_msg);
} else {
throw UnknownResponseCodeException();
}
} else if (server_reply == API_REQUEST_LIMIT_EXCEEDED) {
throw PublicAPIRequestRateExceededException();
} else if (server_reply == FORBIDDEN) {
throw ForbiddenException();
} else {
throw UnknownHttpStatusCodeException();
}
} catch (std::exception& ex) {
QMessageBox message_box{QMessageBox::Warning, "Warning", QObject::tr(ex.what()),
QMessageBox::NoButton, 0, Qt::FramelessWindowHint};
message_box.exec();
}
}
所以问题是我必须把我的 QTimer 放在哪里,它将每 15 秒发送一次retrieveXXXXRequest()
函数,直到它获得我需要的响应代码。
这是github上的链接,用于更深入地理解。
https://github.com/lieroz/VirusTotalClient/tree/develop
所以答案很简单。我将在这里发布一个解决方案。
刚刚更改了网络管理器中的一些行.cpp在请求完成函数中。
void NetworkManager::requestFinished(QNetworkReply* reply) {
try {
http_status_code_t server_reply{static_cast<http_status_code_t>(
reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt())};
if (server_reply == OK) {
QJsonObject json_object = QJsonDocument::fromJson(reply->readAll()).object();
response_code_t response_code{static_cast<response_code_t>(json_object["response_code"].toInt())};
std::string verbose_msg{json_object["verbose_msg"].toString().toStdString()};
if (response_code == ITEM_IS_PRESENT) {
qDebug() << json_object;
if (verbose_msg == "Scan finished, information embedded") {
auto antiviruses = json_object["scans"].toObject().keys();
auto values = json_object["scans"].toObject();
for (auto antivirus : antiviruses) {
qDebug() << antivirus;
auto keys = values.value(antivirus).toObject().keys();
for (auto key : keys) {
qDebug() << key << " : " << values.value(antivirus).toObject().value(key);
}
}
return;
}
QTimer::singleShot(15000, this, [=]{
retrieveFileReportRequest(json_object["resource"].toString());
});
} else {
qDebug() << json_object;
QTimer::singleShot(15000, this, [=]{
retrieveFileReportRequest(json_object["resource"].toString());
});
}
} else if (server_reply == API_REQUEST_LIMIT_EXCEEDED) {
throw PublicAPIRequestRateExceededException();
} else if (server_reply == FORBIDDEN) {
throw ForbiddenException();
} else {
throw UnknownHttpStatusCodeException();
}
} catch (std::exception& ex) {
QMessageBox message_box{QMessageBox::Warning, "Warning", QObject::tr(ex.what()),
QMessageBox::NoButton, 0, Qt::FramelessWindowHint};
message_box.exec();
}
}