目录
概述
核心概念-路由
多线程处理
Qt Https Server
Qt提供了创建简单web服务器的拓展模块,即qthttpserver
关于该模块的一些信息:
Introducing the Qt Http Server
QtHttpServer routing API
该模块的代码位于Qt官方的代码仓库中,但是目前还为正式加入到Qt发行版中,预计Qt6版本会加入该模块。
Qt Project Git Repository Browser
可以看到,存在两个基本相同的代码
qt-extensions/qthttpserver.git - Qt Extension: Qt HTTP Server
qt-labs/qthttpserver.git - Qt HTTP Server
一个归属于Qt扩展模块中,一个归属于Qt实验模块中,尚不清楚具体用意!
Qt http server借鉴了现代大多数流行web服务器的概念,最为典型的是Flask。
使用路由技巧,可以方便地处理一个url的请求。一个路由就是一个特定的URL对应的特定规则,也就是执行特定的回调函数!
静态路由:就是将一个固定的URL路径绑定到一个回调函数
一个例子:
QHttpServer server;
server.route("/blog/", []() {
return "blogs";
});
而且,URL路径是严格的,客户端必须按照规定格式去请求才能得到响应。
如:客户请求:127.0.0.1:12345/blog/ 能得到响应消息,"blogs",
而请求:127.0.0.1:12345/blog 将拒绝服务。
动态路由:就是URL中含有参数,可以动态捕获参数,并将参数转换到程序中需要的类型
如:
server.route( "/blog/" , [] (int year) {
return blogs_by_year(year);
});
Qt http server对带参数的路由规则做了一些改进。URL中不需要像其他框架那样,明确指明参数类型和参数名,因为在C++中不能将捕获的参数绑定到回调参数中,而且也无法对路径格式类型和回调参数进行编译时检查。我们可以使用静态类型。这样我们就可以将编译器用作为“控制器”,确保我们得到的类型是我们支持的类型。
这样客户端请求时只管传入参数(必须得传入参数),如果类型匹配会自动转换得到参数,否则会转换失败,不执行回调函数,拒绝服务。
如:客户请求:127.0.0.1:12345/blog/ 未传入参数,不执行回调函数,无法服务
请求:127.0.0.1:12345/blog/20.21 参数类型有误,不执行回调函数,无法服务
请求:127.0.0.1:12345/blog/2021 参数类型正确,执行回调函数
对于含有多个参数或明确指明参数的情况:
需要使用关键字
如:
server.route( "/blog//" , [] ( int year, int month) { // seconnd is not neccesary
return blogs_by_year_and_month(year, month);
// return QString("query blogs written in %1, %2").arg(year).arg(month);
});
server.route( "/blog//detail" , [] (int year) {
return blogs_by_year(year);
});
另外,还支持创建REST API。为此,我们需要拆分GET/POST/PUT/DELETE请求。
如:
server.route( "/blog/" , QHttpServerRequest::Method::GET, [] ( int year) {
return blogs_by_year(year);
});
这样的话,客户端向该URL发起请求,但是使用POST方法的话,是无法得到响应的。
如果希望自定义HTTP响应的头,则可以使用底层API,QHttpServerResponder。
如:
server.route( "/blog/" , [] ( int year, QHttpServerResponder &&responder) {
responder.write(blogs_by_year(year), "text/plain" );
});
备注:QHttpServerResponder和QHttpServerRequest是特殊参数,只能用作回调函数的最后一个参数。
最后,提供一个使用多线程处理请求的例子以供参考。这里使用了Qt Concurrent命名空间里的多线程,这是一种高级多线程API,详见 Qt的四种多线程讲解。
server.route("/submitReport", QHttpServerRequest::Method::POST, [this](const QHttpServerRequest &request) -> QHttpServerFutureResponse {
auto future = QtConcurrent::run(this, &MyServer::onSubmit, request.body());
return future;
});
QHttpServerResponse MyServer::onSubmit(const QByteArray &request)
{
return QHttpServerResponse(Database::updateSubmit(request));
}
Qt http server还支持创建https server,只需要在listen()之前调用sslSetup()进行QSslSocket设置即可。
需要提前使用openssl生成证书和密钥。
一般使用方法如下:
QHttpServer httpsServer;
QSslConfiguration sslConfiguration;
QFile certFile(QStringLiteral(":/cert/localhost.cert"));
QFile keyFile(QStringLiteral(":/cert/localhost.key"));
certFile.open(QIODevice::ReadOnly);
keyFile.open(QIODevice::ReadOnly);
QSslCertificate certificate(&certFile, QSsl::Pem);
QSslKey sslKey(&keyFile, QSsl::Rsa, QSsl::Pem);
certFile.close();
keyFile.close();
sslConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone);
//sslConfiguration.setProtocol(QSsl::TlsV1_0);
sslConfiguration.setLocalCertificate(certificate);
sslConfiguration.setPrivateKey(sslKey);
httpsServer.sslSetup();
httpsServer.listen(QHostAddress::Any, 6666);
通过查看源码,即可明白缘由。
sslSetup()
listen()