You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Boost Beast:内容长度未知时,如何用自定义JSON body生成非分块响应

你的思路完全没问题,这里有个可行的解决方案

首先得说:你的想法非常合理——内存中的JSON对象和最终要传输的序列化字节流本来就是两个东西,让负责生成字节流的writer来提供实际长度才更贴合逻辑。只不过Boost Beast的默认body模型设计里,size()方法是用来提前获取负载长度、从而避免分块编码的必经之路,所以我们需要调整自定义body的实现来适配这个要求,同时避免重复序列化的开销。

核心解决方案:预序列化缓存

我们可以给自定义body的value_type绑定一个序列化缓存,在size()方法里完成一次序列化并缓存结果,之后writer直接复用这个缓存的内容发送。这样既满足了Beast对size()的要求,又不会重复序列化JSON,还能避免分块编码。

举个具体的实现例子(假设用Boost.Json作为JSON库):

#include <boost/beast.hpp>
#include <boost/json.hpp>

namespace http = boost::beast::http;
namespace json = boost::json;

// 包装JSON对象和序列化缓存的结构体
struct CachedJson {
    json::value data;
    mutable std::optional<std::string> cached_serialized;
};

// 自定义JSON Body类型
class JsonBody {
public:
    // 我们的value_type是带缓存的JSON对象
    using value_type = CachedJson;

    // 静态size方法:计算并缓存序列化后的长度
    static std::uint64_t size(value_type const& val) {
        if (!val.cached_serialized) {
            // 第一次调用时序列化并缓存
            val.cached_serialized = json::serialize(val.data);
        }
        return val.cached_serialized->size();
    }

    // Writer类:直接使用缓存的序列化数据发送
    class writer {
    public:
        template<bool isRequest, class Fields>
        writer(http::header<isRequest, Fields> const&, value_type const& val)
            : val_(val) {}

        void init(boost::system::error_code& ec) {
            ec = {}; // 初始化无错误
        }

        // 同步写入
        template<class SyncWriteStream>
        std::size_t write(SyncWriteStream& stream, boost::system::error_code& ec) {
            // 兜底:如果size()没被调用过,这里补做序列化
            if (!val_.cached_serialized) {
                const_cast<value_type&>(val_).cached_serialized = json::serialize(val_.data);
            }
            return boost::beast::write(stream, boost::beast::buffer(*val_.cached_serialized), ec);
        }

        // 异步写入(如果需要支持异步)
        template<class AsyncWriteStream, class WriteHandler>
        void async_write(AsyncWriteStream& stream, WriteHandler&& handler) {
            if (!val_.cached_serialized) {
                const_cast<value_type&>(val_).cached_serialized = json::serialize(val_.data);
            }
            boost::beast::async_write(
                stream,
                boost::beast::buffer(*val_.cached_serialized),
                std::forward<WriteHandler>(handler)
            );
        }

    private:
        value_type const& val_;
    };

    // Reader类(如果需要解析JSON请求/响应的话)
    class reader {
    public:
        template<bool isRequest, class Fields>
        reader(http::header<isRequest, Fields> const&, value_type& val)
            : val_(val) {}

        void init(boost::system::error_code& ec) {
            ec = {};
        }

        // 同步读取
        template<class SyncReadStream>
        std::size_t read(SyncReadStream& stream, std::uint64_t bytes_remaining, boost::system::error_code& ec) {
            std::string buffer;
            buffer.resize(bytes_remaining);
            auto bytes_read = boost::beast::read(stream, boost::beast::buffer(buffer), ec);
            if (!ec) {
                val_.data = json::parse(buffer, ec);
            }
            return bytes_read;
        }

        // 异步读取(按需实现)
        template<class AsyncReadStream, class ReadHandler>
        void async_read(AsyncReadStream& stream, std::uint64_t bytes_remaining, ReadHandler&& handler) {
            std::string buffer;
            buffer.resize(bytes_remaining);
            boost::beast::async_read(
                stream,
                boost::beast::buffer(buffer),
                [this, handler = std::forward<ReadHandler>(handler), buffer = std::move(buffer)]
                (boost::system::error_code ec, std::size_t bytes_read) mutable {
                    if (!ec) {
                        val_.data = json::parse(buffer, ec);
                    }
                    handler(ec, bytes_read);
                }
            );
        }

    private:
        value_type& val_;
    };
};

使用方式

当你构建HTTP消息时,直接使用这个自定义Body类型:

// 创建请求
http::request<JsonBody> req(http::verb::post, "/api/endpoint", 11);
req.body().data = json::object{{"key", "value"}, {"number", 42}};

// Beast会自动调用JsonBody::size()获取长度,设置Content-Length
// 发送时writer直接用缓存的序列化内容,无需重复序列化
boost::beast::write(stream, req, ec);

为什么这个方案可行?

  1. 完全满足Beast的body模型要求:size()方法提前提供了准确的负载长度,因此Beast会使用Content-Length头而非分块编码。
  2. 避免重复序列化:JSON只在第一次需要长度时序列化一次,之后writer直接复用缓存的结果,性能开销极小。
  3. 贴合你的需求:内存中始终保存解析后的JSON对象,序列化只在必要时发生,而且对外部脚本/测试完全透明,不需要修改它们。

如果你的JSON数据量极大,预序列化缓存会占用过多内存,那可能需要权衡,但大部分场景下这个方案都是最优解。

内容的提问来源于stack exchange,提问作者Vlad.Z

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:48:07