2025年C++下web框架corw的完全使用手册(实现中文支持)

C++下web框架corw的完全使用手册(实现中文支持)corw 是一个开源 轻量化的 c web 库 在使用上与 python 的 flask 是类似的 本文档为 corw 的完整使用文档 含项目配置 基于 cmakelist 路由绑定 返回数据 json 文本 response 对象 静态资源 模板文件 接口请求处理 REST 请求 url 参数绑定 json 请求

大家好,我是讯享网,很高兴认识大家。

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/的最后一部分

小讯
上一篇 2025-03-29 08:20
下一篇 2025-03-25 11:45

相关推荐

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