说起web开发框架,一定会想到java,php,python等快速开发语言,在从多web开发技术中少有使用C++进行开发,虽然C++性能优越,但是由于开发周期长,语言本身的复杂性,不符合当前快速敏捷开发的要求,正因如此也少有web方面的开源框架。
cinatra介绍
github地址:https://github.com/qicosmos/cinatra
cinatra是一个高性能易用的http框架,使用c++17开发,所以编译gcc7以上,本示例在linux平台上使用gcc9.2,windows平台推荐vs2017以上,本示例使用vs2019 community。
cinatra框架支持跨平台并且类拟于boost,整个库header-only因此很容易添加到项目中。整个cinatra框架结构类拟于springmvc,支持面向切面编程。
github上同时有一个使用cinatra框架的web示例,就是feather,github地址 https://github.com/qicosmos/feather
具体的cinatra和feather介绍可到github的项目主页上查看。
首先从github上把feather项目clone下来,项目里包含了windows平台的vs项目文件和cmake文件方便在linux下进行编译。
clone时同时把依赖项clone下来:
cinatra 高性能C++17的http框架
iguana 序列化引擎
ormpp 高性能C++17的ORM框架
render html模板引擎
git clone --recursive https://github.com/qicosmos/feather.git
cinatra是基于boost.asio开发,所以需要使用boost库,虽然介绍中说可以支持ASIO_STANDALONE,不一定需要boost库,但是为了简化示例,这边使用boost最新的库,boost1.72。
介绍一下本人编译环境,
windows平台
windows10,vs2019,mariadb c++连接库,boost1.72,编译为64位程序(64位替换32位是趋势)
linux平台
debian10 buster testing,libmysql,boost1.62, gcc9.2,gcc和boost可直接apt install安装。
代码修改编写均在windows平台的vs2019中完成,在linux下直接cmake即可,这里不详述
由于本人的windows平台安装的是mariadb数据库,没有安装mysql因此需修改ormpp框架中type_mapping.hpp中的头文件包含,以适配mariadb,在此不详述,其他使用与mysql一致。
feather项目中带有数据库初始化脚本feather.sql直接导入数据库即可(先新建数据库用户feather)
在vs中配置完boost库和mariadb conntor库的目录后,可直接编译生成feather.exe文件。把静态文件复制到与feather.exe同一目录即可测试运行。效果如下:
整体上已经有一个网站基本的功能了,包括用户登录,角色,内容管理,新闻管理,公告消息,文件管理等功能。
现在我们在上排导航菜单上再加一个“工作台”,使用H+的前端框架,使用mvc架构让这个“工作台”知道登录的用户和角色,如果没有登录,打开此“工作台”则跳转至登录页面。
我们打开feather项目中的main.cpp文件,在上面可以看到很多现成的http handle,比如:
主页是:
server.set_http_handler<GET>("/", &purecpp_controller::home, &purecpp_ctl, check_start_end_input{});
server.set_http_handler<GET>("/home", &purecpp_controller::home, &purecpp_ctl, check_start_end_input{});
可以看到/home和/ 会跳转到同一个home控制器。
现在我们添加一个自已的http handle,同时添加一个controller用来处理我们的“工作台”
在main.cpp中添加:
server.set_http_handler<GET, POST>("/workdash", &purecpp_controller::workdash, &purecpp_ctl, check_start_end_input{});
在purecpp_controller.hpp中添加控制器:
void workdash(request& req, response& res) {
auto session = req.get_session().lock();
if (session == nullptr || session->get_data<std::string>("user_name").empty()) {
std::cout << "redirect to login page." << std::endl;
render_simple_page(req, res, "./purecpp/html/login.html", "login");
}
else {
render_simple_page(req, res, "./purecpp/html/workdash/index.html", "workdash");
}
}
说明:
feather原本的静态文件夹与exe文件在同一目录,我们的workdash就存放在html目录下新的workdash(把整个h+框架的html复制到此),并且把整个H+的静态文件在 ./purecpp/workdash目录中(并修改相应的html页面)
首先从session中判断是否为登录状态,如果未登录就跳转到登录页面。
如果只是这样,虽然编译完全没问题 ,但是从workdash跳转到登录页面后,无论怎么样,都无法登录,并且提示用户名密码错误。这是什么原因呢?
我们看到登录页面的controller是login_page,提交登录的controller是login,login_page没什么好说的,只是一个页面渲染。
我们看一下login控制器:
void login(request& req,response& res){
const auto& params = req.get_aspect_data();
const auto& user_name = params[0];
const auto& password = params[1];
………………
}
我们看到login控制器中的用户名和密码信息不是从我拉post提交的信息,而是从切面中获取。
切面中的用户名密码信息是怎么来的呢?我们看到在http handle中有个 check_login_input切面
server.set_http_handler<GET, POST>("/login", &purecpp_controller::login, &purecpp_ctl, check_login_input{});
查看切面(在validate.hpp文件中)
struct check_login_input {
bool before(request& req, response& res) {
auto user_name = req.get_query_value("user_name");
auto password = req.get_query_value("password");
if (len_more_than<255>(user_name, password)) {
res.set_status_and_content(status_type::bad_request, "the input parameter is too long");
return false;
}
if (!check_input(res, user_name, password)) {
return false;
}
req.set_aspect_data(sv2s(user_name), sv2s(password));
return true;
}
};
login在处理前,先进入切面把请求中的user_name和password放入切面中。即然切面中已经有我们post的信息,为什么还是无法登录呢?
我们设置断点,发现,req.get_aspect_data()返回一个vector的引用,vector容器中却有4个成员user_name和password分别在第3和第4个,第1和第2个分别是首页新闻的条数和页数信息,默认的login控制器获取的用户名和密码就变成了新闻页码信息。所以我们把login控制器稍作修改:
void login(request& req, response& res) {
//const auto& params = req.get_aspect_data();
//const auto& user_name = params[0];
//const auto& password = params[1];
const auto& user_name = sv2s( req.get_query_value("user_name"));
const auto& password = sv2s(req.get_query_value("password"));
std::string sql = "select ID, user_role from pp_user where user_login='" + user_name +
"' and user_pass=md5('" + password+"')";
Dao dao;
auto r = dao.query<std::tuple<std::string, std::string>>(sql);
if (r.empty()) {
res.set_status_and_content(status_type::ok, "user name or password is not right");
return;
}
std::string user_id = std::get<0>(r[0]);
std::string user_role = std::get<1>(r[0]);
init_session(req, res, user_id, user_role, user_name);
res.redirect("/home");
}
这样从request中直接荼取用户名和密码问题 就解决了,点击workdash后跳转到登录页面就能正常登录。
现在修改前端代码,在导航菜单上添加“工作台”
打开我们复制过去的H+代码,导航菜单在navigator.html。
在“社区服务”的html下添加一个:
<!-- 我们添加的workdash工作台 -->
<li class="layui-nav-item $if category==3 {{layui-this}}">
<a href= "workdash">工作台</a>
打开workdash中的index.html
在<haad>结点,先把样式表等静态资源地址修改我们自已的目录:
<link href="./purecpp/workdash/css/bootstrap.min.css?v=3.3.6" rel="stylesheet">
<link href="./purecpp/workdash/css/font-awesome.min.css?v=4.4.0" rel="stylesheet">
<link href="./purecpp/workdash/css/animate.css" rel="stylesheet">
<link href="./purecpp/workdash/css/style.css?v=4.1.0" rel="stylesheet">
用编辑器搜索功能,把所有的图片,js文件的地址都修改完后。我们修改用户名为登录的用户名
<!--
<span class="block m-t-xs"><strong class="font-bold">Beaut-zihan</strong></span>
-->
<span class="block m-t-xs"><strong class="font-bold">${login_user_name}</strong></span>
现在看一下效果,打开主页面:
登录后点击“工作台”(feather数据库默认管理员用户名是tc密码是123456,这边新建一用户xuly)
页面渲染已经正常。
看,是不是很简单?
cinatra语法使用C++17标准,不建议使用原始指针,大量使用智能指针,提高安全性和稳定性,简单的controller可直接用lambda表达式。并且从以上的例子也看到大量使用类型推导,因此开发难度并不比java和php等常用web开发高多少,并且C++直接编译成本地机器语言,性能方面远超其他web框架。
cinatra开源许可证是宽松的MIT许可证,据说新版本已经支持mq和nosql支持,
其他的cinatra和feather的介绍,可参看github上官方的介绍,这里不在赘述。