Drogon 简介
Drogon 是一个基于 C++17 的高性能 HTTP/Web 框架,它让 C++ 开发 Web 应用变得简单而高效。
基于 C++17 的高性能 HTTP/Web 框架 Drogon。内容包括环境搭建、快速入门示例、路由系统详解、控制器模式、ORM 数据库操作、中间件实现及配置文件使用。通过实战代码展示了如何构建 RESTful API,涵盖异步非阻塞架构、模块化设计及跨平台特性,帮助开发者快速上手 Drogon 进行高性能后端开发。

Drogon 是一个基于 C++17 的高性能 HTTP/Web 框架,它让 C++ 开发 Web 应用变得简单而高效。
以下是在 Ubuntu 系统上的安装步骤:
# 安装依赖
sudo apt-get install cmake g++ git libssl-dev zlib1g-dev libjsoncpp-dev
# 克隆源码
git clone https://github.com/drogonframework/drogon.git
cd drogon
git submodule update --init
# 编译安装
mkdir build
cd build
cmake ..
make -j4
sudo make install
让我们创建一个最简单的 Drogon 应用,实现一个 "Hello World" 服务。
cmake_minimum_required(VERSION 3.10)
project(hello_drogon)
set(CMAKE_CXX_STANDARD 17)
find_package(Drogon REQUIRED)
add_executable(hello_drogon main.cpp)
target_link_libraries(hello_drogon PRIVATE Drogon::Drogon)
#include <drogon/drogon.h>
int main() {
drogon::app().registerHandler("/", [](const drogon::HttpRequestPtr& req, std::function<void (const drogon::HttpResponsePtr&)>&& callback) {
auto resp = drogon::HttpResponse::newHttpResponse();
resp->setBody("Hello, Drogon!");
callback(resp);
});
drogon::app().addListener("0.0.0.0", 8080);
std::cout << "Server running on http://localhost:8080" << std::endl;
drogon::app().run();
return 0;
}
编译运行:
mkdir build && cd build
cmake ..
make ./hello_drogon
访问 http://localhost:8080 将看到响应。
Drogon 的路由系统非常灵活,支持多种路由方式。
#include <drogon/drogon.h>
#include <string>
using namespace drogon;
void initRoutes() {
// 基本路由
app().registerHandler("/", [](const HttpRequestPtr& req, std::function<void (const HttpResponsePtr&)>&& callback) {
auto resp = HttpResponse::newHttpResponse();
resp->setBody("<h1>Home Page</h1>");
resp->setContentTypeCode(CT_TEXT_HTML);
callback(resp);
});
// 带参数的路由
app().registerHandler("/user/{name}", [](const HttpRequestPtr& req, std::function<void (const HttpResponsePtr&)>&& callback, const std::string& name) {
auto resp = HttpResponse::newHttpResponse();
resp->setBody("<h1>Hello, " + name + "!</h1>");
resp->setContentTypeCode(CT_TEXT_HTML);
callback(resp);
});
// 正则表达式路由
app().registerHandler("/product/(\\d+)", [](const HttpRequestPtr& req, std::function<void (const HttpResponsePtr&)>&& callback, const std::string& productId) {
auto resp = HttpResponse::newHttpResponse();
resp->setBody("<h1>Product ID: " + productId + "</h1>");
resp->setContentTypeCode(CT_TEXT_HTML);
callback(resp);
});
// 带 HTTP 方法限定的路由
app().registerHandler("/api/data", [](const HttpRequestPtr& req, std::function<void (const HttpResponsePtr&)>&& callback) {
auto resp = HttpResponse::newHttpResponse();
resp->setBody(R"({"status": "success", "data": "example data"})");
resp->setContentTypeCode(CT_APPLICATION_JSON);
callback(resp);
}, {HttpMethod::Get});
// 路由组
auto userApi = app().createResourceGroup("user_api", "/api/user");
userApi->registerHandler("/profile", [](const HttpRequestPtr& req, std::function<void (const HttpResponsePtr&)>&& callback) {
auto resp = HttpResponse::newHttpResponse();
resp->setBody(R"({"name": "John Doe", "email": "[email protected]"})");
resp->setContentTypeCode(CT_APPLICATION_JSON);
callback(resp);
}, {HttpMethod::Get});
}
对于大型应用,推荐使用控制器模式来组织代码。
#pragma once
#include <drogon/HttpController.h>
using namespace drogon;
class UserController : public HttpController<UserController> {
public:
METHOD_LIST_BEGIN
ADD_METHOD_TO(UserController::getUser, "/user/{id}", HttpMethod::Get);
ADD_METHOD_TO(UserController::createUser, "/user", HttpMethod::Post);
ADD_METHOD_TO(UserController::updateUser, "/user/{id}", HttpMethod::Put);
ADD_METHOD_TO(UserController::deleteUser, "/user/{id}", HttpMethod::Delete);
METHOD_LIST_END
void getUser(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr&)>&& callback, int id) const;
void createUser(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr&)>&& callback) const;
void updateUser(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr&)>&& callback, int id) const;
void deleteUser(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr&)>&& callback, int id) const;
};
#include "UserController.h"
#include <nlohmann/json.hpp>
using json = nlohmann::json;
void UserController::getUser(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr&)>&& callback, int id) const {
json user = {{"id", id}, {"name", "John Doe"}, {"email", "[email protected]"}};
auto resp = HttpResponse::newHttpResponse();
resp->setBody(user.dump());
resp->setContentTypeCode(CT_APPLICATION_JSON);
callback(resp);
}
void UserController::createUser(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr&)>&& callback) const {
auto jsonBody = req->getJsonObject();
if (!jsonBody) {
auto resp = HttpResponse::newHttpResponse();
resp->setStatusCode(k400BadRequest);
resp->setBody(R"({"error": "Invalid request body"})");
resp->setContentTypeCode(CT_APPLICATION_JSON);
callback(resp);
return;
}
json response = {{"status", "success"}, {"message", "User created"}, {"userId", 123}};
auto resp = HttpResponse::newHttpResponse();
resp->setStatusCode(k201Created);
resp->setBody(response.dump());
resp->setContentTypeCode(CT_APPLICATION_JSON);
callback(resp);
}
void UserController::updateUser(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr&)>&& callback, int id) const {
json response = {{"status", "success"}, {"message", "User updated"}, {"userId", id}};
auto resp = HttpResponse::newHttpResponse();
resp->setBody(response.dump());
resp->setContentTypeCode(CT_APPLICATION_JSON);
callback(resp);
}
void UserController::deleteUser(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr&)>&& callback, int id) const {
json response = {{"status", "success"}, {"message", "User deleted"}, {"userId", id}};
auto resp = HttpResponse::newHttpResponse();
resp->setBody(response.dump());
resp->setContentTypeCode(CT_APPLICATION_JSON);
callback(resp);
}
#include <drogon/drogon.h>
#include "UserController.h"
int main() {
drogon::app().addListener("0.0.0.0", 8080);
drogon::app().registerController(std::make_shared<UserController>());
std::cout << "Server running on http://localhost:8080" << std::endl;
drogon::app().run();
return 0;
}
Drogon 内置了强大的 ORM 系统,支持多种数据库。以下是使用 ORM 操作 MySQL 的示例。
#pragma once
#include <drogon/orm/ORM.h>
#include <string>
using namespace drogon::orm;
class User {
public:
std::string name;
std::string email;
int age = 0;
std::string created_at;
DROGON_MAPPING(User, (name, VARCHAR, 50) (email, VARCHAR, 100, UNIQUE) (age, INT) (created_at, TIMESTAMP))
};
#pragma once
#include <drogon/HttpController.h>
#include "User.h"
using namespace drogon;
class DbUserController : public HttpController<DbUserController> {
public:
METHOD_LIST_BEGIN
ADD_METHOD_TO(DbUserController::getAllUsers, "/db/users", HttpMethod::Get);
ADD_METHOD_TO(DbUserController::getUserById, "/db/users/{id}", HttpMethod::Get);
ADD_METHOD_TO(DbUserController::createUser, "/db/users", HttpMethod::Post);
METHOD_LIST_END
void getAllUsers(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr&)>&& callback) const;
void getUserById(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr&)>&& callback, int id) const;
void createUser(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr&)>&& callback) const;
};
#include "DbUserController.h"
#include <nlohmann/json.hpp>
#include <drogon/drogon.h>
using json = nlohmann::json;
using namespace drogon::orm;
void DbUserController::getAllUsers(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr&)>&& callback) const {
auto client = app().getDbClient();
client->execSqlAsync(
"SELECT * FROM users",
[callback](const Result& result) {
json response;
for (const auto& row : result) {
json user;
user["id"] = row["id"].as<int>();
user["name"] = row["name"].as<std::string>();
user["email"] = row["email"].as<std::string>();
user["age"] = row["age"].as<int>();
user["created_at"] = row["created_at"].as<std::string>();
response.push_back(user);
}
auto resp = HttpResponse::newHttpResponse();
resp->setBody(response.dump());
resp->setContentTypeCode(CT_APPLICATION_JSON);
callback(resp);
},
[callback](const DrogonDbException& e) {
auto resp = HttpResponse::newHttpResponse();
resp->setStatusCode(k500InternalServerError);
resp->setBody(json{{"error", e.base().what()}}.dump());
resp->setContentTypeCode(CT_APPLICATION_JSON);
callback(resp);
}
);
}
void DbUserController::getUserById(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr&)>&& callback, int id) const {
auto client = app().getDbClient();
client->execSqlAsync(
"SELECT * FROM users WHERE id = $1",
[callback](const Result& result) {
auto resp = HttpResponse::newHttpResponse();
if (result.size() == 0) {
resp->setStatusCode(k404NotFound);
resp->setBody(json{{"error", "User not found"}}.dump());
resp->setContentTypeCode(CT_APPLICATION_JSON);
callback(resp);
return;
}
const auto& row = result[0];
json user;
user["id"] = row["id"].as<int>();
user["name"] = row["name"].as<std::string>();
user["email"] = row["email"].as<std::string>();
user["age"] = row["age"].as<int>();
user["created_at"] = row["created_at"].as<std::string>();
resp->setBody(user.dump());
resp->setContentTypeCode(CT_APPLICATION_JSON);
callback(resp);
},
[callback](const DrogonDbException& e) {
auto resp = HttpResponse::newHttpResponse();
resp->setStatusCode(k500InternalServerError);
resp->setBody(json{{"error", e.base().what()}}.dump());
resp->setContentTypeCode(CT_APPLICATION_JSON);
callback(resp);
},
id
);
}
void DbUserController::createUser(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr&)>&& callback) const {
auto jsonBody = req->getJsonObject();
if (!jsonBody || !jsonBody->isMember("name") || !jsonBody->isMember("email")) {
auto resp = HttpResponse::newHttpResponse();
resp->setStatusCode(k400BadRequest);
resp->setBody(json{{"error", "Missing required fields"}}.dump());
resp->setContentTypeCode(CT_APPLICATION_JSON);
callback(resp);
return;
}
std::string name = (*jsonBody)["name"].asString();
std::string email = (*jsonBody)["email"].asString();
int age = jsonBody->isMember("age") ? (*jsonBody)["age"].asInt() : 0;
auto client = app().getDbClient();
client->execSqlAsync(
"INSERT INTO users (name, email, age, created_at) VALUES ($1, $2, $3, NOW()) RETURNING id",
[callback](const Result& result) {
auto resp = HttpResponse::newHttpResponse();
resp->setStatusCode(k201Created);
resp->setBody(json{
{"status", "success"},
{"message", "User created"},
{"userId", result[0]["id"].as<int>()}
}.dump());
resp->setContentTypeCode(CT_APPLICATION_JSON);
callback(resp);
},
[callback](const DrogonDbException& e) {
auto resp = HttpResponse::newHttpResponse();
resp->setStatusCode(k500InternalServerError);
resp->setBody(json{{"error", e.base().what()}}.dump());
resp->setContentTypeCode(CT_APPLICATION_JSON);
callback(resp);
},
name, email, age
);
}
#include <drogon/drogon.h>
#include "DbUserController.h"
int main() {
drogon::app().createDbClient("mysql", "localhost", 3306, "username", "password", "test_db");
drogon::app().addListener("0.0.0.0", 8080);
drogon::app().registerController(std::make_shared<DbUserController>());
std::cout << "Server running on http://localhost:8080" << std::endl;
drogon::app().run();
return 0;
}
Drogon 支持中间件,可以用于实现认证、日志、缓存等功能。
#pragma once
#include <drogon/HttpMiddleware.h>
using namespace drogon;
class LoggingMiddleware : public HttpMiddleware<LoggingMiddleware> {
public:
void handler(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr&)>&& callback, NextHandler&& next) override;
};
#include "LoggingMiddleware.h"
#include <chrono>
#include <ctime>
#include <iomanip>
#include <sstream>
void LoggingMiddleware::handler(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr&)>&& callback, NextHandler&& next) {
auto start = std::chrono::high_resolution_clock::now();
auto newCallback = [callback, start, req](const HttpResponsePtr& resp) {
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
auto now = std::chrono::system_clock::now();
std::time_t now_time = std::chrono::system_clock::to_time_t(now);
std::tm* tm = std::localtime(&now_time);
std::stringstream ss;
ss << std::put_time(tm, "%Y-%m-%d %H:%M:%S");
std::cout << "[" << ss.str() << "] " << req->methodString() << " " << req->path() << " " << resp->statusCode() << " " << duration.count() << "µs" << std::endl;
callback(resp);
};
next(req, std::move(newCallback));
}
#pragma once
#include <drogon/HttpMiddleware.h>
using namespace drogon;
class AuthMiddleware : public HttpMiddleware<AuthMiddleware> {
public:
void handler(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr&)>&& callback, NextHandler&& next) override;
};
#include "AuthMiddleware.h"
#include <nlohmann/json.hpp>
using json = nlohmann::json;
void AuthMiddleware::handler(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr&)>&& callback, NextHandler&& next) {
auto authHeader = req->getHeader("Authorization");
if (!authHeader || authHeader->empty()) {
auto resp = HttpResponse::newHttpResponse();
resp->setStatusCode(k401Unauthorized);
resp->setBody(json{{"error", "Authorization header missing"}}.dump());
resp->setContentTypeCode(CT_APPLICATION_JSON);
callback(resp);
return;
}
if (*authHeader != "Bearer secret_token") {
auto resp = HttpResponse::newHttpResponse();
resp->setStatusCode(k403Forbidden);
resp->setBody(json{{"error", "Invalid or expired token"}}.dump());
resp->setContentTypeCode(CT_APPLICATION_JSON);
callback(resp);
return;
}
next(req, std::move(callback));
}
#include <drogon/drogon.h>
#include "AuthMiddleware.h"
#include "LoggingMiddleware.h"
int main() {
drogon::app().registerMiddleware(std::make_shared<LoggingMiddleware>());
auto protectedApi = drogon::app().createResourceGroup("protected_api", "/api/protected")
.addMiddleware(std::make_shared<AuthMiddleware>());
protectedApi->registerHandler("/data", [](const drogon::HttpRequestPtr& req, std::function<void (const drogon::HttpResponsePtr&)>&& callback) {
auto resp = drogon::HttpResponse::newHttpResponse();
resp->setBody(R"({"data": "This is protected data"})");
resp->setContentTypeCode(drogon::CT_APPLICATION_JSON);
callback(resp);
}, {drogon::HttpMethod::Get});
drogon::app().registerHandler("/public/data", [](const drogon::HttpRequestPtr& req, std::function<void (const drogon::HttpResponsePtr&)>&& callback) {
auto resp = drogon::HttpResponse::newHttpResponse();
resp->setBody(R"({"data": "This is public data"})");
resp->setContentTypeCode(drogon::CT_APPLICATION_JSON);
callback(resp);
}, {drogon::HttpMethod::Get});
drogon::app().addListener("0.0.0.0", 8080);
std::cout << "Server running on http://localhost:8080" << std::endl;
drogon::app().run();
return 0;
}
对于复杂应用,推荐使用配置文件来配置 Drogon 应用。
{
"listeners": [
{
"address": "0.0.0.0",
"port": 8080,
"https": false
}
],
"db_clients": [
{
"name": "default",
"type": "mysql",
"host": "localhost",
"port": 3306,
"dbname": "test_db",
"user": "username",
"password": "password",
"connections": 10
}
],
"app": {
"name": "My Drogon App",
"description": "A sample application using Drogon framework",
"version": "1.0.0"
},
"log": {
"level": "info",
"log_to_console": true,
"log_to_file": true,
"log_file": "app.log"
},
"workers": 4
}
#include <drogon/drogon.h>
int main() {
drogon::app().loadConfigFile("config.json");
drogon::app().registerHandler("/", [](const drogon::HttpRequestPtr& req, std::function<void (const drogon::HttpResponsePtr&)>&& callback) {
auto resp = drogon::HttpResponse::newHttpResponse();
resp->setBody("Hello from configured app!");
callback(resp);
});
std::cout << "Server running with config" << std::endl;
drogon::app().run();
return 0;
}
Drogon 框架为 C++ 开发者提供了一个现代化、高性能的后端开发解决方案。它充分利用了 C++17 的新特性,同时提供了简洁易用的 API,让开发者能够快速构建高性能的 Web 应用和 API 服务。
本文介绍了 Drogon 的核心功能,包括路由系统、控制器模式、ORM 数据库操作、中间件和配置文件等。通过这些功能,你可以构建出结构清晰、性能卓越的 C++ 后端应用。
Drogon 的学习曲线虽然比一些脚本语言框架陡峭,但对于需要高性能和低资源消耗的场景,它无疑是一个值得深入学习和使用的优秀框架。
官方文档与源码:

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML 转 Markdown 互为补充。 在线工具,Markdown 转 HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML 转 Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online