Работа с сетью в Qt

QHttpServer: легковесный REST-сервер на Qt без сторонних фреймворков

Введение

Когда нужно быстро поднять HTTP API внутри Qt-приложения — например, для локального управления десктопным приложением через браузер или для тестового стенда — необязательно подключать тяжёлые сторонние фреймворки. Начиная с Qt 6, в составе Qt HTTP Server есть класс QHttpServer, который позволяет описать маршруты в декларативном стиле, похожем на Express.js или Flask. В статье построим простой REST-сервер с несколькими эндпоинтами.

Концепция

QHttpServer регистрирует обработчики маршрутов через route(), указывая шаблон пути, HTTP-метод и лямбда-функцию, которая принимает параметры из пути и возвращает ответ. Сервер сам разбирает HTTP-запросы, работает поверх QTcpServer (или QSslServer для HTTPS) и поддерживает параметры пути, JSON-ответы через QHttpServerResponse и обработку ошибок 404 через setMissingHandler().

Это решение хорошо подходит для встроенных API средней нагрузки — не для высоконагруженных публичных сервисов, где более уместны специализированные C++ HTTP-фреймворки или обратные прокси.

Пример кода: QHttpServer

#include <QCoreApplication>
#include <QHttpServer>
#include <QJsonObject>
#include <QJsonArray>
#include <QTcpServer>

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);

    QHttpServer server;

    server.route("/api/status", QHttpServerRequest::Method::Get, []() {
        QJsonObject obj;
        obj["status"] = "ok";
        obj["version"] = "1.0.0";
        return QHttpServerResponse(QJsonDocument(obj).toJson(),
                                    "application/json");
    });

    server.route("/api/users/<arg>", QHttpServerRequest::Method::Get, [](int userId) {
        QJsonObject obj;
        obj["id"] = userId;
        obj["name"] = QString("User #%1").arg(userId);
        return QHttpServerResponse(QJsonDocument(obj).toJson(),
                                    "application/json");
    });

    server.route("/api/echo", QHttpServerRequest::Method::Post,
                 [](const QHttpServerRequest &request) {
        return QHttpServerResponse(request.body(), "application/json");
    });

    server.setMissingHandler(&server, [](const QHttpServerRequest &, QHttpServerResponder &&responder) {
        responder.write(QByteArray("{\"error\":\"not found\"}"),
                         "application/json",
                         QHttpServerResponder::StatusCode::NotFound);
    });

    auto tcpServer = new QTcpServer(&server);
    if (!tcpServer->listen(QHostAddress::Any, 8080) || !server.bind(tcpServer)) {
        qWarning("Не удалось запустить сервер на порту 8080");
        return 1;
    }

    qInfo("REST-сервер запущен: http://127.0.0.1:8080");
    return app.exec();
}

Пояснения к коду

Каждый вызов route() связывает шаблон URL с обработчиком. Шаблон <arg> автоматически извлекает значение из пути и приводит его к типу параметра лямбды (в примере — int userId). Для POST-запроса /api/echo обработчик принимает QHttpServerRequest целиком и возвращает тело запроса обратно — простой способ показать работу с входящими данными.

setMissingHandler() определяет, что вернуть, если ни один маршрут не подошёл — это аналог обработчика 404 в других фреймворках. Сервер привязывается к явно созданному QTcpServer через bind(), что даёт контроль над адресом и портом прослушивания.

Подводные камни

  • QHttpServer — однопоточный по умолчанию. Долгие синхронные операции в обработчике маршрута блокируют обработку всех остальных запросов — для тяжёлых вычислений выносите работу в QtConcurrent или пул потоков, возвращая ответ asynchronно через QHttpServerResponse с future.
  • Отсутствие встроенной авторизации/rate-limiting. В отличие от полнофункциональных веб-фреймворков, QHttpServer не предоставляет готовых механизмов аутентификации — их нужно реализовывать вручную в каждом обработчике или через middleware-подобную обёртку.
  • Версионная нестабильность API. QHttpServer появился относительно недавно и API менялся между минорными версиями Qt 6 — стоит сверяться с документацией конкретной версии при обновлении Qt.
  • Ограниченная поддержка статических файлов и стриминга. Для раздачи большого количества статики или потокового видео лучше использовать выделенный веб-сервер (nginx) перед Qt-приложением, а QHttpServer оставить для API.