corw是一个开源、轻量化的c++web库,在使用上与python的flask是类似的。本文档为corw的完整使用文档,含项目配置(基于cmakelist)、路由绑定、返回数据(json、文本、response对象、静态资源、模板文件)、接口请求处理(REST请求,url参数绑定、json请求、GET参数和POST参数)和各种高级操作(Cookie操作、Session操作、文件上传操作、文件下载操作、websocket操作、自定义loghandler)。此外,还对各类参数请求、结果返回过程中对中文的支持(如get参数、post参数、url参数、json结果中中文参数的正确解读)
1 基本设置
1.1 Camkelist
项目的cmake文件如下,需要添加对asio、boost库的依赖,其中还补充了msysql库依赖(需要安装mysql)。
cmake_minimum_required(VERSION 3.5.1) #生成生成项目的名称 project(CmakeTest) set_property(GLOBAL PROPERTY USE_FOLDERS ON) set(CMAKE_SUPPRESS_REGENERATION FALSE) #设置生成release项目 set(CMAKE_BUILE_TYPE Release) set(CMAKE_CXX_STANDARD 17) # It prevents the decay to cpp98 when the compiler does not support cpp14 set(CMAKE_CXX_STANDARD_REQUIRED ON) # It disables the use of compiler-specific extensions # e.g. -std=cpp14 rather than -std=gnu++14 set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") file(GLOB SOURCE_FILES src/*.cpp) set(msysql_dir "C:/Program Files/MySQL/MySQL Server 8.0") include_directories( ${
msysql_dir}/include E:/Lib/Crow-master/include E:/Lib/boost_1_77_0 E:/Lib/asio-1.26.0/include ${
PROJECT_SOURCE_DIR}/include ) link_directories( E:/Lib/boost_1_77_0/stage/lib ${
msysql_dir}/lib ${
PROJECT_SOURCE_DIR}/include ) add_executable(${
CMAKE_PROJECT_NAME} ${
SOURCE_FILES}) target_link_libraries( ${
CMAKE_PROJECT_NAME} libmysql.lib ${
Opencv_lib} )
讯享网
1.2 起步设置
起步设置,设置对session的支持,
讯享网 using Session = crow::SessionMiddleware<crow::InMemoryStore>; crow::App<crow::CookieParser, Session> app{
Session{
// customize cookies crow::CookieParser::Cookie("session").max_age(/*one day*/ 24 * 60 * 60).path("/"), // set session id length (small value only for demonstration purposes) 4, // init the store crow::InMemoryStore{
}} }; app.loglevel(crow::LogLevel::Warning);
官网完整起步代码,gitcode地址 https://gitcode.net/mirrors/CrowCpp/Crow
#include "crow.h" int main() {
crow::SimpleApp app; CROW_ROUTE(app, "/")([](){
return "Hello world"; }); app.port(18080).multithreaded().run(); }
1.3 首页绑定
可以用于绑定静态文件
讯享网//首页绑定 CROW_ROUTE(app, "/")([](const crow::request&, crow::response& res) {
res.set_static_file_info("templates/index.html"); res.end(); //return u8"你好 世界!"; });
1.4 静态文件支持
在做路由函数外部,设置静态文件目录,可以实现全局下的静态资源访问
//设置对静态文件的支持 crow::response response; response.set_static_file_info("./");
2、返回各种数据
2.1 返回字符串
讯享网 //返回字符串 CROW_ROUTE(app, "/api1")([]() {
return "Hello world"; });
2.2 返回json
{ {“name”, “lisi”},{“age”,“32”}}对应json字符串{“name”:“lisi”,“age”:“32”},在crow中以以{name,value}构成json中的键值对
//返回json CROW_ROUTE(app, "/json")([] {
crow::json::wvalue x({
{
"message", "Hello, World!"} }); //添加新字段 x["message2"] = "Hello, World.. Again!"; return x.dump(); //return x; }); //返回json数组 CROW_ROUTE(app, "/jsonarr")([] {
crow::json::wvalue x({
{
{
"name", "lisi"},{
"age","32"}}, {
{
"name", "zhangsan"},{
"age","28"}} }); x[0]["message2"] = "new "; return x; });
2.3 返回response对象
前面的各种返回其实也是返回response对象,只是没有显性表示。这里可以实例化response对象,然后设置返回的具体对象
讯享网 //response细节 CROW_ROUTE(app, "/response/<int>") ([](int count) {
//返回404 crow::response resp; resp.code = 404; resp.body = "response body"; //return resp; //返回重定向 crow::response res; res.redirect("/"); //return res; if (count > 100) {
// // response(int code, std::string contentType, std::string body) return crow::response(500); //return crow::response(400); } std::ostringstream os; os << count << " bottles of beer!"; return crow::response(os.str()); });
2.4 返回静态页面
在路由函数中返回静态页面
CROW_ROUTE(app, "/")([](){
auto page = crow::mustache::load_text("fancypage.html"); return page; });
2.5 返回静态资源
在路由函数中返回静态资源文件
讯享网CROW_ROUTE(app, "/imgs/<string>")([](string fname){
string final_path = "static/" + fname + ".jpg"; //cout << final_path << endl; crow::response res; res.set_static_file_info(final_path); return res; });
3、参数请求
3.1 url参数绑定及模板参数注入
模板文件需存储在templates目录下,html文件中的模板参数使用{ {}}进行包裹
<p>Hello {
{user}}</p> <p>体重 {
{count}} kg</p>
url参数在请求时以,等形式描述,用/进行分割,在路由lambda函数的参数中又对相应类型顺序的参数进行命名,以便于在代码中使用
讯享网 //模板参数绑定 CROW_ROUTE(app, "/set_name/<string>/<int>")([](std::string name,int count111) {
auto page = crow::mustache::load("index.html"); //ctx用于模板参数注入,以{name,value}构成参数对,再以{参数对,参数对,...}的形式构成参数集 crow::mustache::context ctx({
{
"user", "{set_name}:"+name},{
"count", count111} }); return page.render(ctx); });
3.2 处理 JSON Requests
CROW_ROUTE(app, "/add_json") .methods("POST"_method) ([](const crow::request& req) {
auto x = crow::json::load(req.body); if (!x) return crow::response(crow::status::BAD_REQUEST); // same as crow::response(400) int sum = x["a"].i() + x["b"].i(); std::ostringstream os; os << sum; return crow::response{
os.str() }; });
3.3 GET参数及POST参数
讯享网//get接口测试 127.0.0.1:18000/getfunc?key1=123&key2=342fsaj&keys=123&keys=sdfs CROW_ROUTE(app, "/getfunc") .methods("GET"_method) ([](const crow::request& req) {
//req.url_params.get("key1") != nullptr string key1=req.url_params.get("key1"); string key2=req.url_params.get("key2"); string keys0=req.url_params.get_list("keys")[0]; string keys1=req.url_params.get_list("keys")[1]; });
POST参数通常是通过表单传递的,表单格式如下:
<form action="/postfunc" method="POST" enctype="multipart/form-data"> 文件:<input type="file" name="myfile00" /><br/> name:<input type="input" name="name" /><br/> size:<input type="input" name="size" /><br/> <input type="submit" value="提交" /> </form>
POST接收数据的后端代码如下所示:
讯享网 CROW_ROUTE(app, "/postfunc") .methods("POST"_method) ([](const crow::request& req) {
crow::multipart::message x(req); //req.url_params.get("key1") != nullptr string name = x.get_part_by_name("name").body; string size = x.get_part_by_name("size").body; string file = x.get_part_by_name("myfile00").body; //post表单中的文件只需要将其以二进制格式写入文件即可实现文件上传 std::string saveFilePath = "upname";// fileName + "." + fileExt; std::ofstream saveFile; //一定要以二进制格式写入文件 saveFile.open(saveFilePath, std::ofstream::binary); saveFile << file; saveFile.close(); });
3.4 REST接口区分测试
在绑定url时同时指定methods参数,然后在函数体内部依据req.method区分具体请求类型
//rest接口测试 CROW_ROUTE(app, "/pathToApi/<int>") .methods("GET"_method, "POST"_method, "DELETE"_method) ([](const crow::request& req, const int& id) {
if (req.method == "GET"_method) {
if ((req.url_params.get("v") != nullptr) & (req.url_params.get("q") != nullptr)) {
// ... } return crow::response(200, "You used GET"); } else if (req.method == "POST"_method) {
return crow::response(200, "You used POST"); } else if (req.method == "DELETE"_method) {
return crow::response(200, "You used DELETE"); } else {
return crow::response(404); } });
4、高级操作
4.1 Cookie操作
讯享网 CROW_ROUTE(app, "/read") ([&](const crow::request& req) {
auto& ctx = app.get_context<crow::CookieParser>(req); // Read cookies with get_cookie auto value = ctx.get_cookie("key"); return "value: " + value; }); CROW_ROUTE(app, "/write") ([&](const crow::request& req) {
auto& ctx = app.get_context<crow::CookieParser>(req); // Store cookies with set_cookie ctx.set_cookie("key", "word") // configure additional parameters .path("/") .max_age(120) .httponly(); return "ok!"; });
4.2 Session操作
在实现session操作时,切记要使用crow::App<crow::CookieParser, Session>方法初始app对象
//session测试 //http://127.0.0.1:18080/session_set?key=a2&value=li%20si //http://127.0.0.1:18080/session_set?key=a2&value=li%20si CROW_ROUTE(app, "/session_set") ([&](const crow::request& req) {
auto& session = app.get_context<Session>(req); //get只能获取url|get参数中的一个 auto key = req.url_params.get("key"); //get_list可以获取url|get参数中多个相同的key的value auto value = req.url_params.get_list("value", false)[0]; session.set(key, value); //在设置session后不能立即使用seesion.get(),要在下一次请求中生效。 return "set "+ std::string(key)+"="+ std::string(value); }); CROW_ROUTE(app, "/session_get") ([&](const crow::request& req) {
auto& session = app.get_context<Session>(req); session.get("key", "not-found"); // get string by key and return "not-found" if not found session.get("int", -1); session.get<bool>("flag"); // returns default value(false) if not found //删除特定session值 session.remove("key"); auto keys = session.keys();// return list of keys std::string out; for (std::string key : keys) // session.string(key) converts a value of any type to a string out += "<p> " + key + " = " + session.get(key, "_NOT_FOUND_"); +"</p>"; return out; });
4.3 上传文件操作
在上传文件时,获取附件名称时存在bug,无法准确的获取中文名称
讯享网 //upload测试 CROW_ROUTE(app, "/uploader") .methods("POST"_method) ([](const crow::request& req) {
crow::multipart::message x(req); std::string upname; std::string fileName; std::string fileExt; auto ss = x.get_part_by_name("myfile00");//获取前端from中input标签中name为myfile00的输入 auto shead = ss.get_header_object("content-disposition"); if (shead.params.size() != 0) {
upname = getFileNameFull(shead.params); //std::string fnf()); int found(upname.find('.')); fileName = std::string(upname.substr(0, found)); fileExt = std::string(upname.substr(found + 1)); } std::string saveFilePath = upname;// fileName + "." + fileExt; std::ofstream saveFile; //一定要以二进制格式写入文件 saveFile.open(saveFilePath, std::ofstream::binary); saveFile << ss.body; saveFile.close(); return upname; });
上述代码中getFileNameFull的实现方式如下
inline std::string getFileNameFull(std::unordered_map<std::string, std::string>& map) {
for (auto pair : map) {
if (pair.first == "filename") {
return pair.second; } } return ""; }
上传文件页面中的表单的写法
讯享网 <form action="/uploader" method="POST" enctype="multipart/form-data"> <input type="file" name="myfile00" /> <input type="file" name="myfile11" /> <input type="submit" value="提交" /> </form>
4.4 文件下载操作
在crow中文件下载与返回静态资源基本上是一样的,只是在返回文件下载流时要设置Content-Type与Content-Disposition,若不设置在输出图片或文本文件时会被浏览器强制解析,而无法弹出下载框。这里实现的下载并不支持多线程分段下载。
//下载图片 CROW_ROUTE(app, "/download/<string>/<string>")([&](string dir,string name) {
string final_path= dir + "/" + name; //cout << final_path << endl; crow::response res; res.set_header("Content-Type","application/octet-stream"); res.set_header("Content-Disposition", "attachment; filename="+ name); res.set_static_file_info(final_path); return res; });
若要实现文件的多线程分段下载,首先要从http请求头中获取Range信息,然后解析出该http请求所对应的文件起始位置和结束位置。在crow中返回的Range是一个字符串,需要自行进行字符串分割,然后将字符串转换为int。
讯享网req.get_header_value("Range");//Range: bytes=0-1199 其中位置0,结束位置1199
res.body = bin2str(bin_data); res.set_header("Content-Length","1200");//响应http请求头中Range的长度 res.set_header("Content-Range","bytes 0-1199/5000");//响应http请求头中Range的位置 res.set_header("Content-Type","application/octet-stream"); res.set_header("Content-Disposition", "attachment; filename="+ name);
前端通过多线程优化图像加载可以参考https://blog.csdn.net/frontend_frank/article/details/
4.5 websocket操作
讯享网#include "crow.h" #include <unordered_set> #include <mutex> int main() {
crow::SimpleApp app; std::mutex mtx; std::unordered_set<crow::websocket::connection*> users; CROW_WEBSOCKET_ROUTE(app, "/ws") .onopen([&](crow::websocket::connection& conn) {
CROW_LOG_INFO << "new websocket connection from " << conn.get_remote_ip(); std::lock_guard<std::mutex> _(mtx); users.insert(&conn); }) .onclose([&](crow::websocket::connection& conn, const std::string& reason) {
CROW_LOG_INFO << "websocket connection closed: " << reason; std::lock_guard<std::mutex> _(mtx); users.erase(&conn); }) .onmessage([&](crow::websocket::connection& /*conn*/, const std::string& data, bool is_binary) {
std::lock_guard<std::mutex> _(mtx); //给当前用户发信息 conn.send_text(data); //给所有用户发信息 for (auto u : users){
if (is_binary){
u->send_binary(data); }else{
u->send_text(data); } } }); CROW_ROUTE(app, "/") ([] {
char name[256]; gethostname(name, 256); crow::mustache::context x; x["servername"] = name; auto page = crow::mustache::load("ws.html"); return page.render(x); }); app.port(18080) .multithreaded() .run(); }
模板代码如下,在templates目录下创建ws.html
<!doctype html> <html> <head> <script src="https://code.jquery.com/jquery-3.1.0.min.js"></script> </head> <body> <input id="msg" type="text"></input> <button id="send"> Send </button><BR> <textarea id="log" cols=100 rows=50> </textarea> <script> var sock = new WebSocket("ws://{
{servername}}:18080/ws"); sock.onopen = ()=>{
console.log('open') } sock.onerror = (e)=>{
console.log('error',e) } sock.onclose = (e)=>{
console.log('close', e) } sock.onmessage = (e)=>{
$("#log").val( e.data +"\n" + $("#log").val()); } $("#msg").keypress(function(e){
if (e.which == 13) {
sock.send($("#msg").val()); $("#msg").val(""); } }); $("#send").click(()=>{
sock.send($("#msg").val()); $("#msg").val(""); }); </script> </body> </html>
4.6 自定义loghandler
使用crow输出log信息时,时区与中国东八区差了8个小时,为修正log信息时间的准确性,可以自定义loghandler;也可也查看logging.h,修改关键代码(如添加#define CROW_USE_LOCALTIMEZONE,使用localtime_s函数生成时间)
讯享网//获取指定格式的时间字符串 inline string get_now_time(string format= "%Y-%m-%d %H:%M:%S") {
time_t rawtime; struct tm* info; char buffer[80]; time(&rawtime); info = localtime(&rawtime); strftime(buffer, 80, format.c_str(), info); string stime(buffer); return stime; } //设置log记录器 class CustomLogger : public crow::ILogHandler {
public: int loglevel; CustomLogger(int loglevel) {
this->loglevel = loglevel; } void log(std::string message, crow::LogLevel level) {
// "message" doesn't contain the timestamp and loglevel // prefix the default logger does and it doesn't end // in a newline. int intlevel = (int)level; if (loglevel >= intlevel) {
string time = get_now_time(); std::cerr << time <<" " << message << std::endl; } } }; //--------------- //绑定log记录器 CustomLogger logger(0); crow::logger::setHandler(&logger); //app.loglevel(crow::LogLevel::Warning);//使用默认loghandler //--------------
5、中文支持
post参数中文支持、 url|get参数中文支持、json结果中文支持请参考https://download.csdn.net/download/a/的最后一部分

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/135975.html