C++Web框架Drogon

kyaa111 6月前 ⋅ 348 阅读

Drogon是一个基于C++14/17的Http应用框架,使用Drogon可以方便的使用C++构建各种类型的Web应用服务端程序。

Drogon的主要应用平台是Linux,也支持Mac OS、FreeBSD和Windows。

它的主要特点如下:

  • 网络层使用基于epoll(macOS/FreeBSD下是kqueue)的非阻塞IO框架,提供高并发、高性能的网络IO。详细请见TFB Tests Results;
  • 全异步编程模式;
  • 支持Http1.0/1.1(server端和client端);
  • 基于template实现了简单的反射机制,使主程序框架、控制器(controller)和视图(view)完全解耦;
  • 支持cookies和内建的session;
  • 支持后端渲染,把控制器生成的数据交给视图生成Html页面,视图由CSP模板文件描述,通过CSP标签把C++代码嵌入到Html页面,由drogon的命令行工具在编译阶段自动生成C++代码并编译;
  • 支持运行期的视图页面动态加载(动态编译和加载so文件);
  • 非常方便灵活的路径(path)到控制器处理函数(handler)的映射方案;
  • 支持过滤器(filter)链,方便在控制器之前执行统一的逻辑(如登录验证、Http Method约束验证等);
  • 支持https(基于OpenSSL实现);
  • 支持websocket(server端和client端);
  • 支持Json格式请求和应答, 对Restful API应用开发非常友好;
  • 支持文件下载和上传,支持sendfile系统调用;
  • 支持gzip/brotli压缩传输;
  • 支持pipelining;
  • 提供一个轻量的命令行工具drogon_ctl,帮助简化各种类的创建和视图代码的生成过程;
  • 基于非阻塞IO实现的异步数据库读写,目前支持PostgreSQL和MySQL(MariaDB)数据库;
  • 基于线程池实现sqlite3数据库的异步读写,提供与上文数据库相同的接口;
  • 支持ARM架构;
  • 方便的轻量级ORM实现,支持常规的对象到数据库的双向映射操作;
  • 支持插件,可通过配置文件在加载期动态拆装;
  • 支持内建插入点的AOP

安装

Ubuntu 20.04

环境

sudo apt install git
sudo apt install gcc
sudo apt install g++
sudo apt install cmake

jsoncpp

sudo apt install libjsoncpp-dev

uuid

sudo apt install uuid-dev

OpenSSL

sudo apt install openssl
sudo apt install libssl-dev

zlib

sudo apt install zlib1g-dev

postgresql

sudo apt-get install postgresql-all

构建

cd $WORK_PATH
git clone https://github.com/an-tao/drogon
cd drogon
git submodule update --init
mkdir build
cd build
cmake ..
make && sudo make install

使用

drogon_ctl create project web-test

在clion中打开

入口函数

#include <drogon/drogon.h>
int main() {
    drogon::app().addListener("0.0.0.0", 80);
    drogon::app().run();
    return 0;
}

创建一个位于demo v1名称空间内且名称为User的控制器

drogon_ctl create controller -h demo::v1::User

demo_v1_User.h如下

#pragma once
#include <drogon/HttpController.h>
using namespace drogon;
namespace demo
{
    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::login,"/token?userId={1}&passwd={2}",Post);
                METHOD_ADD(User::getInfo,"/{1}/info?token={2}",Get);
            METHOD_LIST_END
            //your declaration of processing function maybe like this:
            void login(const HttpRequestPtr &req,
                       std::function<void (const HttpResponsePtr &)> &&callback,
                       std::string &&userId,
                       const std::string &password);
            void getInfo(const HttpRequestPtr &req,
                         std::function<void (const HttpResponsePtr &)> &&callback,
                         std::string userId,
                         const std::string &token) const;
        };
    }
}

demo_v1_User.cc如下

#include "demo_v1_User.h"
using namespace demo::v1;
//add definition of your processing function here

void User::login(const HttpRequestPtr &req,
           std::function<void (const HttpResponsePtr &)> &&callback,
           std::string &&userId,
           const std::string &password)
{
    LOG_DEBUG<<"User "<<userId<<" login";
    //认证算法,读数据库,验证身份等...
    //...
    Json::Value ret;
    ret["result"]="ok";
    ret["token"]=drogon::utils::getUuid();
    auto resp=HttpResponse::newHttpJsonResponse(ret);
    callback(resp);
}
void User::getInfo(const HttpRequestPtr &req,
             std::function<void (const HttpResponsePtr &)> &&callback,
             std::string userId,
            const std::string &token) const
{
    LOG_DEBUG<<"User "<<userId<<" get his information";
    //验证token有效性等
    //读数据库或缓存获取用户信息
    Json::Value ret;
    ret["result"]="ok";
    ret["user_name"]="Jack";
    ret["user_id"]=userId;
    ret["gender"]=1;
    auto resp=HttpResponse::newHttpJsonResponse(ret);
    callback(resp);
}

运行

浏览器访问

http://localhost/demo/v1/user/token?userId=123&passwd=qwe

返回

{
    "result": "ok",
    "token": "48C77F7F6B5F46909ACAEDE6D75DAAC0"
}

http://localhost/demo/v1/user/123456/info?token=8922CE8AE70641AAA4BB03810C40F2F2

返回

{
    "gender": 1,
    "result": "ok",
    "user_id": "123456",
    "user_name": "Jack"
}