
为什么不用 Guzzle 直接发 POST 而要封装 JSON-RPC 客户端
因为 json_encode + curl_exec 看似简单,但漏掉 id 递增、错误响应结构解析、批量请求(batch)支持、超时与重试策略后,很快就会在并发或服务异常时掉坑里。
- JSON-RPC 规范要求每个请求必须带唯一
id,且响应需严格匹配——手写容易忽略类型(intvsstring)导致匹配失败 - 服务返回
{"error": {"code": -32601, "message": "Method not found"}}时,不统一抛出JsonRpcException,业务层就得到处判isset($resp['error']) - PHP 默认 cURL 不自动处理 HTTP 4xx/5xx 的 body,
curl_setopt($ch, CURLOPT_FAILONERROR, true)反而会让 404 直接报错中断,拿不到原始 error 字段
用 ext-json + cURL 写一个最小可用客户端
不依赖任何 Composer 包,只靠 PHP 7.4+ 原生扩展,核心逻辑控制在 50 行内。
- 构造请求体时,
id必须是数字或字符串,不能为null;推荐用microtime(true)+rand(0,999)避免并发冲突 -
Content-Type必须设为application/json,否则某些服务(如 Python 的jsonrpcserver)直接拒收 - 响应解码后必须校验
json_last_error() === JSON_ERROR_NONE,空响应或乱码 JSON 会导致json_decode返回null,进而误判为网络失败
function jsonrpc_call(string $url, string $method, array $params = []): array
{
$id = sprintf('%.0f_%d', microtime(true) * 1000, rand(0, 999));
$payload = json_encode(['jsonrpc' => '2.0', 'method' => $method, 'params' => $params, 'id' => $id]);
<pre class='brush:php;toolbar:false;'>$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_TIMEOUT => 5,
]);
$raw = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$resp = json_decode($raw, true);
if (json_last_error() !== JSON_ERROR_NONE || !isset($resp['id']) || $resp['id'] !== $id) {
throw new RuntimeException("Invalid JSON-RPC response");
}
if (isset($resp['error'])) {
throw new RuntimeException("RPC Error: {$resp['error']['message']} ({$resp['error']['code']})");
}
return $resp['result'] ?? null;}
PHP 服务端怎么快速暴露一个 JSON-RPC 方法(不用框架)
关键不是“怎么写路由”,而是“怎么安全地反序列化并调用”,尤其当 params 是对象或嵌套数组时。
立即学习“PHP免费学习笔记(深入)”;
- 不要用
$_POST,JSON-RPC 请求体是 raw body,必须用file_get_contents('php://input') - 必须校验
content-type是否为application/json,否则可能被表单提交绕过解析逻辑 - 方法名不能直接拼接进
call_user_func,得白名单控制,比如$allowed = ['user.get', 'order.create'],避免任意代码执行 - 如果传入参数含对象,
json_decode($input, true)会转成关联数组,你的方法签名若期望stdClass就得手动转换
遇到 Parse error 或 Invalid Request 错误时先查什么
这类错误基本不出现在业务逻辑里,而是卡在协议层解析阶段,90% 是格式细节没对齐。
- 检查请求体是否有多余逗号、尾部换行、BOM 头——
trim(file_get_contents('php://input'))比直接json_decode更健壮 - 确认
jsonrpc字段值是字符串"2.0",不是数字2.0(PHP 会转成 float,JSON 编码后变成"jsonrpc":2) - 服务端返回的
Content-Type必须是application/json,有些 Nginx 配置会强制覆盖成text/html,导致客户端解析失败 - 批量请求(
batch)必须是数组,哪怕只有一个请求也不能写成对象,否则违反规范
协议越简单,越容易在字段类型、空格、编码这些地方翻车。别急着加日志埋点,先用 curl -v 把原始请求/响应打出来比对一遍。



















