秋雨De blog

  • 首页
  • 留言板
  • 关于
  • rss
秋雨De blog
一个技术小白的个人博客
  1. 首页
  2. 未分类
  3. 正文

手写 C++ Web 服务器:中间件系统设计

2026年3月14日 99点热度 0人点赞 2条评论

在前面的文章中,我们已经实现了一个基础的 HTTP 服务,可以接收请求、解析 HTTP 头并执行对应的业务逻辑。但随着功能逐渐增多,我很快发现一个问题:

很多逻辑其实是通用逻辑,并不属于某一个具体业务。例如:

  • 请求日志记录
  • Cookie 解析
  • Session 管理
  • 参数解析
  • Content-Type 处理
  • 跨域 CORS 处理

如果这些逻辑全部写在业务代码里,代码会迅速变得臃肿,例如:

处理请求
  ├─ 打印日志
  ├─ 解析 Cookie
  ├─ 创建 Session
  ├─ 解析参数
  ├─ 业务逻辑
  └─ 构建响应

随着功能增加,代码会越来越难维护。

因此,大多数 Web 框架都会提供一种机制:Middleware(中间件)。

在很多流行框架中,例如 Express.js、Koa 或 Gin,中间件都是核心设计之一。

本篇是手写 C++ Web 服务器系列第四篇,本篇我们来实现一套简单的中间件系统,源码地址:https://github.com/Fall-Rain/asio_web_service

一、中间件的核心思想

中间件本质上是一条处理链。

一个请求进入服务器后,会依次经过多个处理阶段:

Client Request
        │
        ▼
   Middleware 1
        │
        ▼
   Middleware 2
        │
        ▼
   Middleware 3
        │
        ▼
   Business Handler
        │
        ▼
Response

每个中间件都可以:

  1. 处理请求
  2. 修改请求
  3. 决定是否继续执行下一个中间件
  4. 在业务执行之后再处理响应

这种设计通常称为:

责任链模式(Chain of Responsibility)

二、中间件接口设计

首先我们定义一个统一的中间件接口:

class middleware {
public:
    virtual ~middleware() = default;

    virtual void handle(std::shared_ptr<Session> session,
                        std::function<void()> next) = 0;
};

这里有两个关键参数:

session

用于访问:

  • request
  • response
  • socket
  • session_id

next()

用于执行下一个中间件。

如果某个中间件不调用 next(),请求链就会在这里终止。

三、中间件执行机制

在 Session 中,我们维护了一个中间件列表:

std::vector<std::shared_ptr<middleware>> middlewares_;

服务器初始化时注册中间件:

Session::Session(boost::asio::ip::tcp::socket &socket)
    : client_socket_(std::move(socket)) {
    add_middleware(std::make_shared<process_params_middleware>());
    add_middleware(std::make_shared<process_content_type_middleware>());
    add_middleware(std::make_shared<log_middleware>());
    add_middleware(std::make_shared<cros_middleware>());
    add_middleware(std::make_shared<cookie_middleware>());
    add_middleware(std::make_shared<session_middleware>());
}

当请求解析完成后,会执行:

run_middlewares();

核心逻辑如下:

void Session::run_next(size_t index) {
    if (index < middlewares_.size()) {
        middlewares_[index]->handle(
            shared_from_this(),
            [this, index] {
                run_next(index + 1);
            });
    } else {
        response = business_logic::process_request(request);
    }
}

执行流程其实非常简单:

middleware[0]
↓
middleware[1]
↓
middleware[2]
↓
business_logic

每个中间件调用 next() 时,才会进入下一个。

四、中间件的执行顺序

当前服务器注册的中间件顺序如下:

process_params_middleware
process_content_type_middleware
log_middleware
cros_middleware
cookie_middleware
session_middleware

请求流程:

HTTP Request
│
▼
解析 URL 参数
│
解析 Content-Type
│
打印请求日志
│
CORS 处理
│
Cookie 解析
│
Session 管理
│
业务处理
│
Response

五、日志中间件

日志中间件用于记录请求和响应。

class log_middleware : public middleware {
    void handle(std::shared_ptr<Session> session, std::function<void()> next) override {         
                  std::cout << "请求:" << std::endl
                  << session->request.to_http_string()
                  << std::endl;        next();        std::cout << "应答:" << std::endl
                  << session->response.to_http_string()
                  << std::endl;
    }
};

注意这里的执行顺序:

打印请求
↓
next()
↓
打印响应

这样就可以同时记录:

  • 请求内容
  • 返回结果

六、Cookie 中间件

Cookie 中间件负责解析请求中的 Cookie。

auto header_cookie = session->request.headers.find("Cookie");

如果存在 Cookie:

Cookie: session=abc123; user=fallrain

则解析为:

session -> abc123
user -> fallrain

并写入:

request.cookie

响应阶段,如果有 Cookie 需要返回:

Set-Cookie: session=xxxx

七、Session 中间件

Session 中间件负责:

  1. 检查是否存在 session
  2. 如果没有则创建
  3. 将 session_id 写入 request

核心逻辑:

auto it = session->request.cookie.find("session");

如果没有:

create_session_map()

如果有但失效:

重新创建 session

最后将 session 写入响应:

Set-Cookie: session=xxxx

八、Content-Type 处理

HTTP 请求体可能是多种格式:

application/json
application/xml
multipart/form-data

中间件会自动解析这些内容:

JSON:

boost::property_tree::read_json

XML:

boost::property_tree::read_xml

FormData:

解析 multipart boundary。

最终解析结果写入:

request.ptree
request.form_data

业务代码可以直接使用。


九、中间件设计的优势

引入中间件后,服务器架构变得非常清晰:

HTTP解析
│
▼
Middleware Chain
│
▼
Business Logic

优点:

  1. 模块解耦
  2. 逻辑复用
  3. 可插拔设计
  4. 代码结构清晰

新增功能只需要新增一个中间件。

例如:

  • JWT 鉴权
  • 限流
  • API 日志
  • 监控

十、总结

在这个 C++ Web 服务器中,中间件系统成为整个框架的核心扩展机制。

通过简单的接口设计和责任链模式,我们实现了一个非常灵活的请求处理流程。

最终形成了这样的架构:

TCP Socket
│
HTTP Parser
│
Middleware Chain
│
Business Logic
│
HTTP Response
本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: 暂无
最后更新:2026年3月14日

fallrain

种一棵树最好的时间是十年前,其次是现在。

点赞
< 上一篇
下一篇 >

文章评论

  • 陈祈星

    这个确实可以学习一下 感谢大佬分享

    2026年3月15日
    回复
    • fallrain

      @陈祈星 推荐你看一下最新的一篇模板化的中间件链设计,这个更符合C++的风格

      2026年3月16日
      回复
  • razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
    回复 陈祈星 取消回复

    fallrain

    种一棵树最好的时间是十年前,其次是现在。

    文章目录
    • 一、中间件的核心思想
    • 二、中间件接口设计
    • 三、中间件执行机制
    • 四、中间件的执行顺序
    • 五、日志中间件
    • 六、Cookie 中间件
    • 七、Session 中间件
    • 八、Content-Type 处理
    • 九、中间件设计的优势
    • 十、总结
    友情连接
    猫饭范文泉博客迎風别葉CODING手艺人ScarSu博友圈
    归档
    • 2026 年 3 月
    • 2025 年 11 月
    • 2025 年 5 月
    • 2025 年 4 月
    • 2025 年 3 月
    • 2024 年 12 月
    • 2024 年 10 月
    • 2024 年 5 月
    • 2023 年 2 月
    • 2022 年 11 月
    • 2022 年 3 月
    • 2021 年 12 月
    • 2021 年 8 月
    • 2021 年 5 月
    • 2021 年 4 月
    • 2021 年 3 月
    • 2020 年 12 月
    • 2020 年 11 月
    • 2020 年 8 月
    • 2020 年 5 月
    • 2019 年 12 月
    • 2019 年 3 月

    吉ICP备18007356号

    吉公网安备22020302000184号

    Theme Kratos Made By Seaton Jiang

    COPYRIGHT © 2026 秋雨De blog ALL RIGHTS RESERVED