CROW基本功能说明

目录

Different parts of Crow 

4.Templating (Mustache) 模板引擎

5.Multipart

6.Query Strings(查询字符串,一般指的是url中"?"后的部分)

7.Middleware(中间件)

8.Blueprints(蓝图功能)(代码已经测试)

9.Websockets(双通)

 Using CROW

        1.Included Middlewares(包含中间件) (已测试) 


Different parts of Crow 

4.Templating (Mustache) 模板引擎

        允许你返回带有自定义数据的 HTML 页面,Crow 支持 Mustache 模板引擎,通过 crow::mustache 实现。
        实现主要包括两个组件:
        Page:这是模板文件,包含 HTML 结构和占位符,用于插入动态数据。
        Context:上下文对象,包含要插入模板中的数据。

        ①其中Page 指的是包含Mustache标签的HTML模板文件,模板文件通常加载到 crow::mustache::template_t 对象中,并且需要放在名为 templates 的目录中,该目录应位于 Crow 可执行文件的当前工作目录内。

        ②使用crow::mustache::set_base("new_templates_directory")来为特定路由设置模板目录; 使用crow::mustache::set_global_base("new_templates_directory") 来全局设置模板目录。

include "crow.h"

int main() {
    crow::SimpleApp app;

    // 全局设置模板目录
    crow::mustache::set_global_base("global_templates");

    CROW_ROUTE(app, "/")
    ([]{
        crow::mustache::context ctx;
        ctx["name"] = "Global Directory";
        auto page = crow::mustache::load("example.html");
        return page.render(ctx);
    });

    // 为这个特定路由设置不同的模板目录
    CROW_ROUTE(app, "/special")
    ([]{
        crow::mustache::set_base("special_templates");
        crow::mustache::context ctx;
        ctx["name"] = "Special Directory";
        auto page = crow::mustache::load("example.html");
        return page.render(ctx);
    });

    app.port(18080).multithreaded().run();
}

默认情况下,所有路由都会使用 global_templates 目录来查找模板,也就是说会默认在global_template文件夹下查找;

但是 /special 路由会在 special_templates 目录中查找模板文件。

           ③在 Crow 中,crow::mustache::context是一个 JSON 对象,用于在模板中传递数据。可以将数据作为键值对添加到context中。

1.假设有一个模板文件 templates/example.html:
<p>{{greeting}}</p>
<p>{{custom_message}}</p>


2.#include "crow.h"

int main() {
    crow::SimpleApp app;

    CROW_ROUTE(app, "/")
    ([]{
        crow::mustache::context ctx;  // crow::mustache::context 对象
        ctx["greeting"] = "Hello, World!";
        
        // 使用 Lambda 表达式来生成动态内容
        ctx["custom_message"] = [](std::string name){
            return "Welcome to Crow, " + name + "!";
        };

        auto page = crow::mustache::load("example.html");
        return page.render(ctx); // Crow 模板系统中的一个方法,用于将模板与上下文数据结合在一起,并生成最终的 HTML 内容。
    });

    app.port(18080).multithreaded().run();
}


5.Multipart

        ①Multipart一种 HTTP 请求或响应的格式,允许在一次请求中包含多个不同的数据部分,这些部分可能有不同的数据类型。

POST /upload HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary

------WebKitFormBoundary
Content-Disposition: form-data; name="text_field"

Hello, World!

------WebKitFormBoundary
Content-Disposition: form-data; name="file1"; filename="example1.txt"
Content-Type: text/plain   //表示了该部分的内容类型为纯文本

(This is the content of example1.txt)

------WebKitFormBoundary
Content-Disposition: form-data; name="file2"; filename="example2.jpg"
Content-Type: image/jpeg   //表示该部分的内容是JPEG格式的图片数据。

(binary content of example2.jpg)
------WebKitFormBoundary--


1.Header: Content-Type: multipart/form-data; boundary=----WebKitFormBoundary 
  表示这是一个multipart 请求,boundary 是各部分的分隔符。
2.Content-Disposition:指定内容的展示方式。form-data 表示它是表单提交的数据。
3.name:指定表单字段的名称。在服务器端,可以使用这个名称来访问该字段的数据。
4.filename:指定上传文件的原始名称。

        ②在Crow中,可以使用 crow::multipart::messagecrow::multipart::message_view                 来处理 multipart 请求和响应。crow::multipart::message 负责管理消息的内容,而                 crow::multipart::message_view 提供对各个部分的视图访问(也就是只读权限)。

#include "crow.h"

int main() {
    crow::SimpleApp app;

    CROW_ROUTE(app, "/upload").methods(crow::HTTPMethod::POST)([](const crow::request& req){
        crow::multipart::message msg(req);

        for (auto& part : msg.parts) {
            std::string part_name = part.name;
            std::string part_filename = part.filename;
            std::string part_content = part.body; // 获取文件内容
        }

        return "Files received";
    });

    app.port(18080).multithreaded().run();
}

6.Query Strings(查询字符串,一般指的是url中"?"后的部分)

    可以通过crow::request::url_params来处理查询字符串

get(name): 根据给定的键获取对应的值。如果找不到键,返回 nullptr。

pop(name): 与 get 类似,但获取值后会将其从查询字符串中移除。由于 url_params 是 const,你需要复制它才能使用 pop。

get_list(name): 获取一个键对应的多个值,返回一个 std::vector<std::string>。例如,处理 URL ?key[]=value1&key[]=value2。

pop_list(name): 与 get_list 类似,但会移除获取的所有值。

get_dict(name): 从查询字符串中获取键值对,返回一个 std::unordered_map<std::string, std::string>。适用于处理类似 ?key[sub_key]=value 的查询字符串。

pop_dict(name): 与 get_dict 类似,但会移除获取的键值对。

7.Middleware(中间件)

        在 Crow 框架中,中间件是一种在处理客户端请求之前或之后执行代码的机制。你可以用它来在请求到达处理程序之前检查或修改请求内容,或者在响应发送回客户端之前做一些处理。

        1.主要组成:
                ①Context(上下文):用于存储请求期间的临时数据
                ②before_handle():在请求处理之前执行,可以在这里进行设置默认值等操作。
                ③after_handle():在请求处理程序之后运行,通常用于修改响应或记录日志。

        2. 工作原理:

                ①请求处理前 (before_handle):
                  当服务器接收到请求时,Crow 首先会执行所有注册的 Middleware 的 before_handle                   方法。可以在这里对请求进行预处理,比如检查用户身份、验证参数、记录日志等。

                ②路由处理
                  请求通过所有 before_handle 方法后,会到达相应的路由处理函数,并生成 响应。

                ③响应处理后(after_handle):
                
路由处理完毕后,Crow 会执行所有 Middleware 的 after_handle 方法。你可以在这                 里对响应进行修改,如添加额外的响应头、记录响应日志等。

        3.实例说明:
                 ①基本处理:访问当前中间件的上下文

假设开发一个博客应用,你希望在每次有人提交评论时,检查他们是否被标记为垃圾评论用户(比如以前的评论中过于频繁地使用某些词汇)。你可以使用中间件来实现:

struct SpamFilterMiddleware {
    struct context {
        bool is_spammer = false;
    };

    // 在处理请求之前,检查用户是否是垃圾评论者
    void before_handle(crow::request& req, crow::response& res, context& ctx) {
        if (req.url_params.get("user") == "spammer123") {
            ctx.is_spammer = true;
            res.code = 403;
            res.end("You are banned from commenting.");
        }
    }

    // 在处理请求之后记录日志
    void after_handle(crow::request& req, crow::response& res, context& ctx) {
        if (ctx.is_spammer) {
            CROW_LOG_INFO << "Blocked a spammer: " << req.url_params.get("user");
        }
    }
};

// 注册中间件
crow::App<SpamFilterMiddleware> app;

         ②模板处理:访问其他中间件的上下文

假设有两个中间件,一个用于验证用户身份(AuthMiddleware),另一个用于记录用户行为(LoggingMiddleware)。在 LoggingMiddleware 中访问 AuthMiddleware 的上下文,以获取用户身份信息并记录相关日志。
struct AuthMiddleware {
    struct context {
        std::string user_id;
        bool authenticated = false;
    };

    void before_handle(crow::request& req, crow::response& res, context& ctx) {
        // 伪代码:检查用户是否登录
        ctx.authenticated = true;
        ctx.user_id = "user123";  // 假设用户已登录,并设置 user_id
    }
};

struct LoggingMiddleware {
    struct context {};

    template <typename AllContext>
    void before_handle(crow::request& req, crow::response& res, context& ctx, AllContext& all_ctx) {
        auto& auth_ctx = all_ctx.template get<AuthMiddleware>();
        if (auth_ctx.authenticated) {
            CROW_LOG_INFO << "User " << auth_ctx.user_id << " is making a request.";
        } else {
            CROW_LOG_INFO << "Unauthenticated request.";
        }
    }
};

             ③并且可以在同一个APP中注册多个中间件。

crow::App<AuthMiddleware, LoggingMiddleware> app;
AuthMiddleware 和 LoggingMiddleware 会按照定义的顺序依次执行。LoggingMiddleware 可以通过模板签名访问 AuthMiddleware 的上下文,从而实现跨中间件的数据共享和处理。

             ④全局中间件(会对所有请求生效,无论请求的路径是什么。)和本地中间件(只会在访问 /local 路由时生效,而不会影响 /global 路由。)

#include "crow.h"

// 全局中间件
struct GlobalMiddleware {
    struct context {};

    void before_handle(crow::request& req, crow::response& res, context& ctx) {
        CROW_LOG_INFO << "Global Middleware: Before handling request";
    }

    void after_handle(crow::request& req, crow::response& res, context& ctx) {
        CROW_LOG_INFO << "Global Middleware: After handling request";
    }
};

// 本地中间件
struct LocalMiddleware : public crow::ILocalMiddleware {
    struct context {};

    void before_handle(crow::request& req, crow::response& res, context& ctx) {
        CROW_LOG_INFO << "Local Middleware: Before handling request";
    }

    void after_handle(crow::request& req, crow::response& res, context& ctx) {
        CROW_LOG_INFO << "Local Middleware: After handling request";
    }
};

int main() {
    // 创建一个 Crow 应用并注册全局中间件
    crow::App<GlobalMiddleware> app;

    // 定义一个带有本地中间件的路由
    CROW_ROUTE(app, "/local")
    .CROW_MIDDLEWARES(app, LocalMiddleware)
    ([]() {
        return "This route has local middleware!";
    });

    // 定义一个不带本地中间件的全局路由
    CROW_ROUTE(app, "/global")
    ([]() {
        return "This route only has global middleware!";
    });

    // 启动服务器
    app.port(18080).multithreaded().run();
}

8.Blueprints(蓝图功能)(代码已经测试)

        1.基本概念:一种类似 Flask 风格的功能,用于模块化管理 Crow 应用。它们不能独立处理网络请求,但可以定义路由,使得应用程序更易于组织和扩展,可以进行模块化开发。

         2.基本使用方式:

             ①基本流程:定义Blueprint--->注册Blueprint

#include crow_all.h

int main() {
    crow::SimpleApp app;

    初始化蓝图
    crow::Blueprint sub_bp("bp2", "csstat", "cstemplate"); 

    // 在 Blueprint 中定义路由
    CROW_BP_ROUTE(sub_bp, "/1")         
        ([]() {
        return "456";
            });

    // 在应用程序中定义路由
    CROW_ROUTE(app, "/2")
        ([]() {
        return "ASD";
            });
 

    // 注册蓝图到应用程序中
    app.register_blueprint(sub_bp);

    // 设置路由

    app.port(18080).multithreaded().run();
}

注意事项: CROW_BP_ROUTE直接使用会报错,因为这种宏设计上需要在特定的函数或代码块中执行,通常是在初始化过程中或者在某个函数中定义蓝图时使用。应该将蓝图的路由配置放入一个函数和类中,并在程序调用该函数。确保蓝图实例正确构建,并且上下文完整。

           ②实现模块化的具体方式

//可以将不同的模块注册成蓝图,之后通过包含头文件来执行



项目结构
project/
│
├── main.cpp               主执行
├── blue1.h       蓝图模块1
├── blue1.cpp
├── blue2.h    蓝图模块2
├── blue2t.cpp


//blue1.h文件          蓝图模块1
#pragma once  
#include "crow_all.h"
class Blue1 {
public:
    crow::Blueprint bp;
    Blue1();
};



//blue1.cpp文件
#include "blue1.h"
//用类去封装独立的蓝图
Blue1::Blue1() : bp("test1") {    
    CROW_BP_ROUTE(bp, "/1")([] {
        return "蓝图模块1.1";
        });
    CROW_BP_ROUTE(bp, "/2")([] {
        return "蓝图模块1.2";
        });
}


//blue2.h               蓝图模块2
#pragma once
#include "crow_all.h"
class Blue2 {
public:
    crow::Blueprint bp;
    Blue2();
};



/blue2.cpp
#include "blue2.h"
Blue2::Blue2() : bp("test2") {
    CROW_BP_ROUTE(bp, "/3")([] {
        return "蓝图模块2.1";
        });
    CROW_BP_ROUTE(bp, "/4")([] {
        return "蓝图模块2.2";
        });
}





/main.cpp 
#include "crow_all.h"
#include "blue1.h"
#include "blue2.h"
int main()
{
	crow::SimpleApp app;
	Blue1 blue1;
	Blue2 blue2;
	app.register_blueprint(blue1.bp);
	app.register_blueprint(blue2.bp);
	app.port(18080).multithreaded().run();
}



             ③蓝图可以有自己的静态目录和模板目录

#include "crow.h"

crow::Blueprint user_bp("user", "user_static", "user_templates");

其中,静态文件:user_bp Blueprint 会从 user_static 目录中查找静态文件
     
     模板文件:user_bp Blueprint 会从 user_templates 目录加载模板文件。




int main() {

    CROW_BP_ROUTE(user_bp, "/profile")
([]() {
    return crow::mustache::load("profile.html").render();
});

    crow::SimpleApp app;

    app.register_blueprint(user_bp);

    app.port(18080).multithreaded().run();
}

如果你不指定的话,就会默认设定静态模板目录为static,html文件目录为templates,和非蓝图路由一致。

              ④可以在蓝图中定义捕获所有路由,处理未匹配的请求

CROW_BP_CATCHALL_ROUTE(parent_bp)
([](const crow::request& req) {
    return "Parent Blueprint Catchall Route";
});
当请求路径 /parent/unknown_path 无法匹配时,捕获所有路由会被触发,返回 "Parent Blueprint Catchall Route"。

              ⑤ 蓝图可以实现嵌套:可以在一个 Blueprint 中注册另一个 Blueprint。这样,子 Blueprint 的路由会继承父 Blueprint 的前缀。例如,如果父 Blueprint 的前缀是 /parent,子 Blueprint 的前缀是 /sub,那么最终的路由会是 /parent/sub/route_path

#include crow_all.h



int main() {
    crow::Blueprint order_bp("order");

    CROW_BP_ROUTE(order_bp, "/status")
        ([]() {
        return "Order Status Page";
            });

    // 定义产品管理的父 Blueprint,并注册订单管理 Blueprint
    crow::Blueprint product_bp("product");

    CROW_BP_ROUTE(product_bp, "/list")
        ([]() {
        return "Product List Page";
            });

    product_bp.register_blueprint(order_bp);
    crow::SimpleApp app;

    // 注册产品管理 Blueprint
    app.register_blueprint(product_bp);

    app.port(18080).multithreaded().run();
}
//访问形式:http://localhost:18080/product/order/status 子蓝图

9.Websockets(双通)

        ① 启用websocket时定义路由的方式

#include "crow.h"

int main() {
    crow::SimpleApp app;

    CROW_WEBSOCKET_ROUTE(app, "/ws")
    .onaccept([&](const crow::request& req, void** userdata) {
        return true;  // 接受 WebSocket 连接
    })
    .onopen([&](crow::websocket::connection& conn) {
        CROW_LOG_INFO << "WebSocket 连接已打开";
    })
    .onmessage([&](crow::websocket::connection& conn, const std::string& message, bool is_binary) {
        CROW_LOG_INFO << "收到消息: " << message;
        conn.send_text("收到的消息: " + message);  // 回显消息
    })
    .onerror([&](crow::websocket::connection& conn, const std::string& error_message) {
        CROW_LOG_ERROR << "WebSocket 错误: " << error_message;
    })
    .onclose([&](crow::websocket::connection& conn, const std::string& reason, uint16_t status_code) {
        CROW_LOG_INFO << "WebSocket 关闭, 原因: " << reason << ", 状态码: " << status_code;
    });

    app.port(18080).multithreaded().run();
}

 Using CROW

        1.Included Middlewares(包含中间件) (已测试) 

            在Crow中提供几种可以直接使用的中间件文件

            ①session会话中间件(在Crow-master\examples\middlewares中可以找到他的cpp文件)  



int main() {
    // Define the session middleware using an in-memory store
    using Session = crow::SessionMiddleware<crow::InMemoryStore>;
    crow::App<crow::CookieParser, Session> app{ Session() };

   // 设置键值对
    CROW_ROUTE(app, "/set")
        ([&](const crow::request& req) {
        auto& session = app.get_context<Session>(req);
        session.set("username", "admin");
        return "Username set to admin";
            });

   // 通过key获取值
    CROW_ROUTE(app, "/get")
        ([&](const crow::request& req) {
        auto& session = app.get_context<Session>(req);
        std::string username = session.get("username", "Not found");
        return "Username: " + username;
            });
   //删除
    // Remove a value from the session
    CROW_ROUTE(app, "/remove")
        ([&](const crow::request& req) {
        auto& session = app.get_context<Session>(req);
        session.remove("username");
        return "Username removed";
            });

    // Start the server
    app.port(18080).multithreaded().run();
    return 0;
}

1.http://localhost:18080/set?key=username&value=admin。
设置键 username 为值 admin。

2.http://localhost:18080/get?key=username。
返回之前设置的值 "admin"。

3.http://localhost:18080/remove?key=username。
删除 username 键

            ②cookie中间件-CookieParser 中间件用于读取和写入 cookies。当启用这个中间件后,它会解析所有传入的 cookies。

int main()
{
	crow::App<crow::CookieParser> app;
	const std::string valid_username = "operator";
	const std::string valid_password = "securepass";
    CROW_ROUTE(app, "/login")
        ([&](const crow::request& req, crow::response& res) {
        std::string username = req.url_params.get("username") ? req.url_params.get("username") : "";
        std::string password = req.url_params.get("password") ? req.url_params.get("password") : "";

        if (username == valid_username && password == valid_password) {
            auto& ctx = app.get_context<crow::CookieParser>(req);
            ctx.set_cookie("authenticated", "true")
                .path("/")
                .max_age(3600);  // 有效期 1 小时
            res.write("Login successful!");
        }
        else {
            res.code = 401;  // 未授权
            res.write("Invalid username or password.");
        }
        res.end();
            });
    CROW_ROUTE(app, "/protected")
        ([&](const crow::request& req, crow::response& res) {
        auto& ctx = app.get_context<crow::CookieParser>(req);
        std::string auth = ctx.get_cookie("authenticated");

        if (auth == "true") {
            res.write("Welcome to the protected area!");
        }
        else {
            res.code = 403;  // 禁止访问
            res.write("Access denied. Please log in first.");
        }
        res.end();
            });

    // 退出登录,清除认证 Cookie
    CROW_ROUTE(app, "/logout")
        ([&](const crow::request& req, crow::response& res) {
        auto& ctx = app.get_context<crow::CookieParser>(req);
        ctx.set_cookie("authenticated", "")
            .path("/")
            .max_age(-1);  // 删除 Cookie
        res.write("Logged out successfully.");
        res.end();
            });

    app.port(18080).multithreaded().run();
    return 0;
}


1.访问 http://localhost:18080/login?username=operator&password=securepass 登录。
2.访问 http://localhost:18080/protected 查看受保护内容,如果已登录成功,显示欢迎信息,否则,会显示“拒绝访问”。
3.访问 http://localhost:18080/logout 退出登录。

                ③cors(用于跨域访问的)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值