Drogon: An Introduction and Practice of the High-Performance C++ Web Framework
Drogon is a high-performance HTTP web application framework written in C++14/17/20, designed to help developers easily build various types of web application server programs. It adopts an asynchronous non-blocking architecture, utilizing efficient event handling mechanisms such as epoll (Linux) and kqueue (macOS/FreeBSD), maintaining excellent performance even under a large number of concurrent requests. Drogon is cross-platform, supporting various operating systems including Linux, macOS, FreeBSD, OpenBSD, and Windows.
Core Features of the Drogon Framework
Drogon boasts a range of powerful features that make it stand out in modern web development:
- Fully Asynchronous Programming Model: Based on non-blocking I/O and callback mechanisms, effectively handling high-concurrency requests
- Flexible Routing System: Supports RESTful style routing design, allowing easy definition of various HTTP endpoints
- Support for Multiple Protocols: Fully supports HTTP/1.0/1.1, WebSocket, HTTPS, and file uploads/downloads
- Database Integration: Provides asynchronous read/write capabilities for PostgreSQL, MySQL, and SQLite3, with a built-in lightweight ORM implementation
- View Rendering: Supports backend rendering, generating dynamic HTML pages through CSP (similar to JSP) templates
- JSON Handling: Built-in JSON serialization and deserialization, ideal for developing RESTful APIs
- Filter Mechanism: Provides AOP support, allowing easy implementation of unified logic such as login validation and logging
- Plugin System: Supports installation and configuration of plugins via configuration files
Installation and Environment Configuration
The steps to install Drogon on Ubuntu are as follows:
# Install dependencies
sudo apt-get install git cmake make g++ libjsoncpp-dev uuid-dev libsqlite3-dev zlib1g-dev libcurl4-openssl-dev libssl-dev
# Install Drogon from source
git clone https://github.com/drogonframework/drogon.git
cd drogon
git submodule update --init
mkdir build && cd build
cmake ..
make
sudo make install
# Verify installation
drogon_ctl version
On other operating systems, you can also install via package managers; for example, on Debian, you can use apt-get install libdrogon-dev.
Creating a Simple Web Service
Below is a simple Drogon application example that creates an HTTP service returning a JSON response:
#include <drogon/drogon.h>
using namespace drogon;
int main() {
// Set HTTP route
app().registerHandler(
"/hello",
[](const HttpRequestPtr& req,
std::function<void(const HttpResponsePtr)>& callback) {
// Create JSON response
Json::Value json;
json["message"] = "Hello, Drogon!";
json["status"] = "success";
auto resp = HttpResponse::newHttpJsonResponse(json);
callback(resp);
},
{Get});
// Configure and start server
app().setLogPath("./")
.setLogLevel(trantor::Logger::kWarn)
.addListener("0.0.0.0", 8080)
.setThreadNum(16)
.run();
return 0;
}
This simple example demonstrates the basic usage of Drogon:
- Register a handler function to the path
/hello - Return a JSON response when a GET request is received
- The server listens on port 8080, using 16 processing threads
Controllers: Structuring Request Handling
For more complex applications, Drogon provides the concept of controllers to better organize code. You can quickly create a controller using the drogon_ctl tool:
drogon_ctl create controller TestCtrl
This will generate header and implementation files:
TestCtrl.h:
#pragma once
#include <drogon/HttpSimpleController.h>
using namespace drogon;
class TestCtrl : public drogon::HttpSimpleController<TestCtrl>
{
public:
virtual void asyncHandleHttpRequest(const HttpRequestPtr &req,
std::function<void (const HttpResponsePtr &)> &callback) override;
PATH_LIST_BEGIN
// Path definitions
PATH_ADD("/test", Get);
PATH_ADD("/test/{1}", Get);
PATH_LIST_END
};
TestCtrl.cc:
#include "TestCtrl.h"
void TestCtrl::asyncHandleHttpRequest(const HttpRequestPtr &req,
std::function<void (const HttpResponsePtr &)> &callback)
{
// Write application logic
auto resp = HttpResponse::newHttpResponse();
resp->setStatusCode(k200OK);
resp->setContentTypeCode(CT_TEXT_HTML);
resp->setBody("Hello from TestCtrl!");
callback(resp);
}
Filters: Implementing AOP Aspect-Oriented Programming
Drogon’s filter functionality allows you to execute common logic before requests reach the handler, such as authentication:
class LoginFilter : public drogon::HttpFilter<LoginFilter>
{
public:
virtual void doFilter(const HttpRequestPtr &req,
FilterCallback &fcb,
FilterChainCallback &fccb) override
{
// Check if the user is logged in
auto session = req->getSession();
if (session->find("user")) {
// User is logged in, continue processing
fccb();
} else {
// User is not logged in, return error
Json::Value json;
json["error"] = "Unauthorized";
json["message"] = "Please login first";
auto resp = HttpResponse::newHttpJsonResponse(json);
resp->setStatusCode(k401Unauthorized);
fcb(resp);
}
}
};
Then register the filter in the controller:
PATH_LIST_BEGIN
PATH_ADD("/profile", Get, "LoginFilter");
PATH_LIST_END
Database Integration and ORM
Drogon has built-in ORM support, making it easy to interact with databases. Here is an example using ORM:
#include <drogon/orm/DbClient.h>
#include <drogon/orm/Mapper.h>
// User model class
class User : public drogon::orm::BaseModel<User>
{
public:
struct PrimaryKey {
int id;
PrimaryKey(int value = 0) : id(value) {}
};
int id;
std::string name;
std::string email;
// Mapping relationship
struct relation {
static const char table_name[];
static const char primary_key[];
static const bool auto_increment = true;
};
// Field definitions
static constexpr const char* column_list = "id, name, email";
};
// Using in handler
app().registerHandler("/users/{id}",
[](const HttpRequestPtr& req,
std::function<void(const HttpResponsePtr&)>& callback,
int user_id) {
auto client = drogon::app().getDbClient();
// Asynchronous query for user
client->execSqlAsync(
"SELECT * FROM users WHERE id = $1",
[callback](const drogon::orm::Result& result) {
if (result.empty()) {
auto resp = HttpResponse::newHttpResponse();
resp->setStatusCode(k404NotFound);
callback(resp);
return;
}
Json::Value json;
json["id"] = result[0]["id"].as<int>();
json["name"] = result[0]["name"].as<std::string>();
json["email"] = result[0]["email"].as<std::string>();
auto resp = HttpResponse::newHttpJsonResponse(json);
callback(resp);
},
[callback](const drogon::orm::DrogonDbException& e) {
// Handle database error
Json::Value json;
json["error"] = "Database error";
json["message"] = e.base().what();
auto resp = HttpResponse::newHttpJsonResponse(json);
resp->setStatusCode(k500InternalServerError);
callback(resp);
},
user_id
);
},
{Get});
Building a RESTful API
Combining the above features, we can build a complete RESTful API:
#include <drogon/drogon.h>
using namespace drogon;
class UserAPI : public HttpSimpleController<UserAPI>
{
public:
void asyncHandleHttpRequest(const HttpRequestPtr& req,
std::function<void(const HttpResponsePtr&)>& callback) override
{
auto method = req->getMethod();
Json::Value response;
if (method == Get) {
// Get user list
response["users"] = Json::arrayValue;
response["count"] = 0;
} else if (method == Post) {
// Create new user
auto json = req->getJsonObject();
if (json) {
response["status"] = "user created";
response["id"] = 1001;
} else {
response["error"] = "invalid input";
}
} else if (method == Put) {
// Update user
response["status"] = "user updated";
} else if (method == Delete) {
// Delete user
response["status"] = "user deleted";
}
auto resp = HttpResponse::newHttpJsonResponse(response);
callback(resp);
}
PATH_LIST_BEGIN
PATH_ADD("/api/users", Get, Post, Put, Delete);
PATH_LIST_END
};
Configuration File
Drogon supports configuring applications via JSON files:
config.json:
{
"app": {
"log_path": "./",
"log_level": "WARN"
},
"listeners": [
{
"address": "0.0.0.0",
"port": 8080,
"https": false
}
],
"db_clients": [
{
"name": "default",
"type": "postgresql",
"host": "localhost",
"port": 5432,
"dbname": "mydatabase",
"user": "myuser",
"passwd": "mypassword"
}
],
"thread_num": 16
}
Load the configuration in code:
app().loadConfigFile("../config.json");
app().run();
Conclusion
The Drogon framework provides C++ developers with a powerful tool for building high-performance web services through its asynchronous non-blocking architecture and rich feature set. Whether for simple API endpoints or complex enterprise-level applications, Drogon can deliver excellent performance and development experience.