Drogon 框架完全指南:C++ 后端开发的新选择
目录
Drogon(中文常被开发者亲切地称为 "龙")是一个基于 C++17 的高性能 HTTP/Web 框架,它让 C++ 开发 Web 应用变得简单而高效。本文将全面介绍 Drogon 框架的核心特性、使用方法和实战案例,帮助你快速上手这个强大的后端框架。
Drogon 简介
Drogon 是一个跨平台的 C++ 后端框架,具有以下特点:
- 基于 C++17 标准,充分利用现代 C++ 特性
- 异步非阻塞架构,性能卓越
- 支持 HTTP 1.0/1.1,WebSocket
- 内置 ORM 系统,轻松操作数据库
- 支持 RESTful API 开发
- 模块化设计,易于扩展
- 轻量级,低资源消耗
环境搭建
首先,我们需要安装 Drogon 框架。以下是在 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 应用
让我们创建一个最简单的 Drogon 应用,实现一个 "Hello World" 服务。
CMakeLists.txt
cmake_minimum_required(VERSION 3.10) project(hello_drogon) # 设置C++标准 set(CMAKE_CXX_STANDARD 17) # 查找Drogon库 find_package(Drogon REQUIRED) # 添加可执行文件 add_executable(hello_drogon main.cpp) # 链接Drogon库 target_link_libraries(hello_drogon PRIVATE Drogon::Drogon) main.cpp
#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,你将看到 "Hello, Drogon!" 的响应。
路由系统详解
Drogon 的路由系统非常灵活,支持多种路由方式:
routes.cpp
#include <drogon/drogon.h> #include <string> using namespace drogon; void initRoutes() { // 1. 基本路由 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); }); // 2. 带参数的路由 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); }); // 3. 正则表达式路由 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); }); // 4. 带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}); // 5. 路由组 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}); userApi->registerHandler("/update", [](const HttpRequestPtr& req, std::function<void (const HttpResponsePtr&)>&& callback) { // 处理用户更新逻辑 auto resp = HttpResponse::newHttpResponse(); resp->setBody(R"({"status": "updated"})"); resp->setContentTypeCode(CT_APPLICATION_JSON); callback(resp); }, {HttpMethod::Post}); } int main() { app().addListener("0.0.0.0", 8080); initRoutes(); std::cout << "Server running on http://localhost:8080" << std::endl; app().run(); return 0; } 控制器模式
对于大型应用,推荐使用控制器模式来组织代码:
UserController.h
#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; }; UserController.cpp
#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} // 假设生成的用户ID }; 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); } main.cpp
#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; } ORM 与数据库操作
Drogon 内置了强大的 ORM 系统,支持多种数据库。以下是使用 ORM 操作 MySQL 的示例:
首先创建模型类 User.h
#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) ) }; DbUserController.h
#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; }; DbUserController.cpp
#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 ); } main.cpp
#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 支持中间件,可以用于实现认证、日志、缓存等功能:
LoggingMiddleware.h
#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; }; LoggingMiddleware.cpp
#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)); } AuthMiddleware.h
#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; }; AuthMiddleware.cpp
#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) { // 检查Authorization头 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)); } main.cpp
#include <drogon/drogon.h> #include "AuthMiddleware.h" #include "LoggingMiddleware.h" int main() { // 全局中间件 - 所有请求都会经过日志中间件 drogon::app().registerMiddleware(std::make_shared<LoggingMiddleware>()); // 路由级中间件 - 只有/api/protected/*路径会经过认证中间件 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 应用:
config.json
{ "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 } main.cpp
#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 的学习曲线虽然比一些脚本语言框架陡峭,但对于需要高性能和低资源消耗的场景,它无疑是一个值得深入学习和使用的优秀框架。
如果你想了解更多关于 Drogon 的信息,可以访问其官方网站和 GitHub 仓库: