想要自己做一个类似 Apache 的服务器软件,因为是某个 Qt 项目中的模块,于是使用了 QHttpServer 库,使用起来也很方便:

1
2
3
4
5
6
7
QHttpServer *server = new QHttpServer;
connect(server, SIGNAL(newRequest(QHttpRequest*, QHttpResponse*)), this, SLOT(serverHandle(QHttpRequest*, QHttpResponse*)));

if (!server->listen(port)) // port是端口变量
{
statusLabel->setText("开启服务端失败!");
}

这样,每当浏览器访问 localhost:port 的时候,则会进入 serverHandle 槽。

1
2
3
4
5
6
7
8
void MainWindow::requestHandle(QHttpRequest *req, QHttpResponse *resp)
{
auto url = req->url().toString();
auto method = req->method();
auto &headers = req->headers();
auto body = req->body();
/* 做一些处理 */
}

但是当收到 post 的数据,想要获取 req->body() 的时候,会发现取到的结果总是空的。

经过一番研究,发现 request 是以流的形式传输,如果不调用 storeBody,那么 request 就不会尝试下载 body 的数据。

调用 req->storeBody() 后,当数据存储结束时,会触发 end()data(const&QByteArray) 信号。

尝试使用 QEventLoop:

1
2
3
4
req->storeBody();
QEventLoop loop;
connect(req, &QHttpRequest::end, &loop, &QEventLoop::quit);
loop.exec();

但是这种发现 req->storeBody() 并不会单独开一个线程,loop->exec() 后会导致 store 堵住,无法实现。

因此,还是只能采用信号槽的形式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class RequestBodyHelper : public QObject
{
Q_OBJECT
public:
RequestBodyHelper(QHttpRequest* req, QHttpResponse* resp)
: req(req), resp(resp)
{
}

void waitBody()
{
req->storeBody();
connect(req, SIGNAL(end()), this, SLOT(end()));
}

public slots:
void end()
{
// qInfo() << "httprequest.end.body: " << req->body();
emit finished(req, resp);
this->deleteLater();
}

signals:
void finished(QHttpRequest* req, QHttpResponse* resp);

private:
QHttpRequest* req;
QHttpResponse* resp;
};
1
2
3
4
5
6
7
8
9
10
11
void MainWindow::serverHandle(QHttpRequest *req, QHttpResponse *resp)
{
if (req->method() == QHttpRequest::HttpMethod::HTTP_GET)
{
// GET方法不用获取数据
return requestHandle(req, resp);
}
RequestBodyHelper *helper = new RequestBodyHelper(req, resp);
helper->waitBody();
connect(helper, SIGNAL(finished(QHttpRequest *, QHttpResponse *)), this, SLOT(requestHandle(QHttpRequest *, QHttpResponse *)));
}

这样就能使用 req->body() 来获取 post 的数据了。