drogonframework / drogon 简要介绍和使用方法说明

1,105 阅读5分钟

Build Status Build Status Total alerts Join the chat at https://gitter.im/drogon-web/community Join the telegram group at https://t.me/joinchat/_mMNGv0748ZkMDAx Docker image

英语 |简体中文|繁體中文

概述

Drogon是一个基于C++14/17的HTTP应用框架。Drogon可用于使用C++轻松构建各种类型的网络应用服务器程序。Drogon是美国电视剧《权力的游戏》中一条龙的名字,我非常喜欢。

Drogon是一个跨平台的框架,它支持Linux、macOS、FreeBSD、OpenBSD、HaikuOS和Windows。它的主要特点如下。

  • 使用基于epoll的非阻塞I/O网络库(macOS/FreeBSD下为kqueue),提供高并发、高性能的网络IO,更多细节请访问TFB测试结果
  • 提供一个完全异步的编程模式。
  • 支持Http1.0/1.1(服务器端和客户端)。
  • 基于模板,实现了简单的反射机制,将主程序框架、控制器和视图完全解耦。
  • 支持cookies和内置会话。
  • 支持后端渲染,控制器生成的数据给视图生成Html页面。视图由CSP模板文件描述,C++代码通过CSP标签嵌入到Html页面。而drogon命令行工具自动生成C++代码文件进行编译。
  • 支持视图页面动态加载(运行时动态编译和加载)。
  • 提供从路径到控制器处理程序的方便灵活的路由解决方案。
  • 支持过滤链,方便在处理HTTP请求前执行统一的逻辑(如登录验证、Http Method约束验证等)。
  • 支持https(基于OpenSSL)。
  • 支持WebSocket(服务器端和客户端)。
  • 支持JSON格式的请求和响应,对Restful API应用开发非常友好。
  • 支持文件下载和上传。
  • 支持gzip、brotli压缩传输。
  • 支持管道化。
  • 提供一个轻量级的命令行工具drogon_ctl,以简化Drogon中各种类的创建和视图代码的生成。
  • 支持基于非阻塞I/O的异步读写数据库(PostgreSQL和MySQL(MariaDB)数据库)。
  • 支持基于线程池的异步读写 sqlite3 数据库。
  • 支持Redis的异步读和写。
  • 支持ARM架构。
  • 提供方便的轻量级ORM实现,支持常规对象到数据库的双向映射。
  • 支持插件,可以在加载时由配置文件安装。
  • 支持带有内置连接点的AOP。
  • 支持C++ Coroutines

一个非常简单的例子

与大多数C++框架不同,Drogon应用程序的主程序可以保持干净和简单。Drogon使用一些技巧将控制器与主程序解耦。控制器的路由设置可以通过宏或配置文件完成。

下面是一个典型的Drogon应用程序的主程序。

#include <drogon/drogon.h>
using namespace drogon;
int main()
{
    app().setLogPath("./")
         .setLogLevel(trantor::Logger::kWarn)
         .addListener("0.0.0.0", 80)
         .setThreadNum(16)
         .enableRunAsDaemon()
         .run();
}

它可以通过使用配置文件进一步简化,如下所示。

#include <drogon/drogon.h>
using namespace drogon;
int main()
{
    app().loadConfigFile("./config.json").run();
}

Drogon提供了一些接口,可以直接在main()函数中添加控制器逻辑,例如,用户可以在Drogon中注册一个类似的处理器。

app().registerHandler("/test?username={name}",
                    [](const HttpRequestPtr& req,
                       std::function<void (const HttpResponsePtr &)> &&callback,
                       const std::string &name)
                    {
                        Json::Value json;
                        json["result"]="ok";
                        json["message"]=std::string("hello,")+name;
                        auto resp=HttpResponse::newHttpJsonResponse(json);
                        callback(resp);
                    },
                    {Get,"LoginFilter"});

虽然这样的接口看起来很直观,但它们并不适合复杂的业务逻辑场景。假设有几十个甚至几百个处理程序需要在框架中注册,那么在各自的类中分别实现它们不是更好的做法吗?所以除非你的逻辑非常简单,否则我们不建议使用上述接口。相反,我们可以创建一个HttpSimpleController,如下所示。

/// The TestCtrl.h file
#pragma once
#include <drogon/HttpSimpleController.h>
using namespace drogon;
class TestCtrl:public drogon::HttpSimpleController<TestCtrl>
{
public:
    void asyncHandleHttpRequest(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback) override;
    PATH_LIST_BEGIN
    PATH_ADD("/test",Get);
    PATH_LIST_END
};

/// The TestCtrl.cc file
#include "TestCtrl.h"
void TestCtrl::asyncHandleHttpRequest(const HttpRequestPtr& req,
                                      std::function<void (const HttpResponsePtr &)> &&callback)
{
    //write your application logic here
    auto resp = HttpResponse::newHttpResponse();
    resp->setBody("<p>Hello, world!</p>");
    resp->setExpiredTime(0);
    callback(resp);
}

上述大多数程序可以由drogon提供的命令行工具drogon_ctl (命令是drogon_ctl create controller TestCtrl自动生成。用户所需要做的就是添加自己的业务逻辑。在这个例子中,当客户端访问http://ip/test URL时,控制器返回一个Hello, world! 字符串。

对于JSON格式的响应,我们创建控制器的方法如下。

/// The header file
#pragma once
#include <drogon/HttpSimpleController.h>
using namespace drogon;
class JsonCtrl : public drogon::HttpSimpleController<JsonCtrl>
{
  public:
    void asyncHandleHttpRequest(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback) override;
    PATH_LIST_BEGIN
    //list path definitions here;
    PATH_ADD("/json", Get);
    PATH_LIST_END
};

/// The source file
#include "JsonCtrl.h"
void JsonCtrl::asyncHandleHttpRequest(const HttpRequestPtr &req,
                                      std::function<void(const HttpResponsePtr &)> &&callback)
{
    Json::Value ret;
    ret["message"] = "Hello, World!";
    auto resp = HttpResponse::newHttpJsonResponse(ret);
    callback(resp);
}

让我们更进一步,用HttpController类创建一个演示的RESTful API,如下所示(省略源文件)。

/// The header file
#pragma once
#include <drogon/HttpController.h>
using namespace drogon;
namespace api
{
namespace v1
{
class User : public drogon::HttpController<User>
{
  public:
    METHOD_LIST_BEGIN
    //use METHOD_ADD to add your custom processing function here;
    METHOD_ADD(User::getInfo, "/{id}", Get);                  //path is /api/v1/User/{arg1}
    METHOD_ADD(User::getDetailInfo, "/{id}/detailinfo", Get);  //path is /api/v1/User/{arg1}/detailinfo
    METHOD_ADD(User::newUser, "/{name}", Post);                 //path is /api/v1/User/{arg1}
    METHOD_LIST_END
    //your declaration of processing function maybe like this:
    void getInfo(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, int userId) const;
    void getDetailInfo(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, int userId) const;
    void newUser(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, std::string &&userName);
  public:
    User()
    {
        LOG_DEBUG << "User constructor!";
    }
};
} // namespace v1
} // namespace api

如你所见,用户可以使用HttpController ,同时映射路径和参数。这是创建RESTful API应用的一种非常方便的方式。

此外,你还可以发现,所有的处理程序接口都是异步模式,响应由一个回调对象返回。这种设计是出于性能的考虑,因为在异步模式下,drogon应用程序可以用少量的线程处理大量的并发请求。

在编译了上述所有的源文件后,我们得到了一个非常简单的Web应用程序。这是一个好的开始。欲了解更多信息,请访问wikiDocsForge

贡献

我们欢迎每一个贡献。请参考贡献指南以了解更多信息。