rawBody()只能调用一次,因底层流式读取机制导致数据流消费后不可回溯;Workerman 4.1+可通过设置Worker::$enableRawBodyCache = true启用缓存实现多次读取。

Workerman 4 的 HTTP 请求中,Request 对象的 rawBody() 方法默认只能调用一次——第二次调用会返回空字符串或引发异常。这不是 bug,而是底层流式读取机制决定的:原始 socket 数据流被消费后即不可回溯。
为什么 rawBody() 只能读一次?
Workerman 的 Request 对象在解析请求时,会将原始 body 字节一次性读入内存并缓存为 $this->rawContent;但 rawBody() 方法内部实际是调用 $this->getBody(),而后者每次都会尝试从底层 InputStream(即 $this->stream)重新读取——此时流已关闭或指针已达末尾,导致后续调用失败。
常见表现:
- 日志切面中调用
$request->rawBody()记录原始请求,Controller 再调用就拿不到数据 - 鉴权拦截器读了一次 body,后续业务逻辑解析 JSON 失败
-
$request->post()和$request->rawBody()混用时,其中一个失效
推荐方案:启用内置可重读 Body 支持
Workerman 4.1+ 已原生支持 body 缓存与复用。只需在 Worker 启动前开启配置:
关键一步:设置 enableRawBodyCache = true
$worker = new Worker('http://0.0.0.0:8080');
$worker->onMessage = function (TcpConnection $connection, Request $request) {
// 此时 rawBody() 可安全多次调用
$body1 = $request->rawBody();
$body2 = $request->rawBody(); // ✅ 不为空,内容一致
$connection->send("OK");
};
// 启用 rawBody 缓存(必须放在 onMessage 定义之前)
Worker::$enableRawBodyCache = true;
Worker::runAll();
该配置会让 Workerman 在首次解析请求时,自动将原始 body 缓存到 $request->rawContent,所有后续 rawBody() 调用均从此缓存读取,不依赖底层流。
兼容旧版本或需手动控制的写法
若无法升级或需精细控制缓存时机,可手动提取并复用原始 body:
- 首次调用
$request->rawBody()获取字节,并显式缓存 - 后续全部使用缓存变量,不再调用
rawBody() - 注意:不要混用
rawBody()和post()或json(),因后者可能触发内部流读取
示例:
$raw = $request->rawBody(); // 第一次,真实读取
if (empty($raw)) {
$raw = file_get_contents('php://input'); // 兜底(仅当 enableRawBodyCache=false 时有效)
}
// 后续全部用 $raw,例如:
$json = json_decode($raw, true);
log::info("req", ['body' => $raw]);
避坑提醒
以下操作会破坏 body 可重读性:
- 在
onMessage中直接调用file_get_contents('php://input')—— 这会强制消耗底层流 - 重复调用
$request->post()(尤其 Content-Type 非 form-data 时,内部可能反复尝试读流) - 未设
Worker::$enableRawBodyCache = true就依赖多次rawBody()
启用缓存后,rawBody()、json()、post() 均可安全组合使用,底层统一走缓存。

















