做接口联调这么多年,我见过太多同事把 JSON 和 JSON-RPC 当成同一个东西在讨论。有人拿一个 JSON 配置文件过来问"这是不是 JSON-RPC 接口",也有人对着 REST 接口说"这不就是返回了个 JSON 嘛"。严格讲,JSON 是一种轻量级的数据交换格式,JSON-RPC 是一种基于 JSON 编码的远程过程调用协议,两者压根不在同一个层次上,但在实际项目里又经常结伴出现。这篇文章我会把两者的定义边界、协议细节、适用场景、代码实操和我在真实环境里踩过的坑一起讲透,适合后端开发、测试工程师,以及刚接触微服务、RPC 通信的入门读者。
1. JSON 是什么:先分清"数据格式"和"通信协议"
1.1 为什么说 JSON 只是"数据的写法"而不是通信规则
先忘掉协议,只看 JSON 本身。JSON 全称 JavaScript Object Notation,最初是 JavaScript 里对象字面量的写法,后来因为简单、易读、跨语言,被收编成一种独立的数据交换格式。它规定了一件事:数据在文本里应该长什么样。对象用花括号包,数组用方括号包,键值对用冒号分隔,字符串必须用双引号,多层嵌套用缩进或压缩形式表达,仅此而已。
这就好比中文语法只规定"句子怎么写才通顺",但它不规定"你说完一句话之后,对方必须在几秒内回答,回答时必须带编号"。JSON 同样如此:它是一种数据表示法,不是对话规则。所以你会看到同一份 JSON 数据,既能被 Python 读取,也能被 Java 读取,但这份数据本身不会告诉你"我该发给谁""发出后怎么知道对方收到了""出错后谁来兜底"。这些内容不是 JSON 能回答的,必须由上层协议来定。
我见过不少新人拿着一个 .json 配置文件问"这算不算 JSON-RPC",其实就是卡在这个认知上:把"数据的形态"当成了"通信的方式"。后面我们讲的 JSON-RPC,恰恰是在 JSON 这个语法基础上补上了"对话规则"的那套东西。
1.2 六种值类型与序列化的底层逻辑
JSON 的完整类型系统就只有六个:对象(object)、数组(array)、字符串(string)、数字(number)、布尔(boolean)、空值(null)。它没有日期类型,没有二进制类型,没有 undefined,也没有注释。这六种类型看起来简单,但正是"少"才带来了跨语言兼容性——任何现代编程语言都能在这六种类型上找到自己的映射。
实操里最容易翻车的点是数字精度。JSON 规范里的 number 对应的是 IEEE 754 双精度浮点数,能精确表示的整数范围只有 -2^53 ~ 2^53。如果你的业务里有一个 18 位的订单号或雪花 ID,直接塞进 JSON 再交给 JavaScript 或某些弱类型处理端,尾数就可能悄悄变成 ...000。所以很多团队在约定接口规范时都会强制要求:超过 16 位的整数一律转成字符串传输。这不是 JSON 的问题,而是使用 JSON 的人必须建立的额外约定。
再说序列化和反序列化。序列化就是把内存对象变成 JSON 文本,反序列化则相反。这个过程看起来机械,里面却藏着很多边界情况:日期对象怎么输出、循环引用的对象怎么处理、字典的 key 是否强制转字符串、空值要不要保留。Python 的 json 模块遇到 datetime 直接抛 TypeError,提示你 Object of type datetime is not JSON serializable,这就是典型的"JSON 没有日期类型"带来的连锁反应,后面第 5 章我会专门展开。
1.3 JSON 在实际项目中最常见的三种用途
第一种是配置文件。细数一下你会发现,前端项目的 package.json、VS Code 的 launch.json、很多工具的 settings.json,全都是 JSON 格式。它的好处显而易见:人眼可读、机器可解析、支持嵌套结构。另一个相关的格式是 JSONC,它允许写注释,适合本地配置文件;标准 JSON 不支持注释,你要么忍受没注释,要么改用 JSONC 或 JSON5。
第二种是接口数据交换。REST API 的请求体和响应体、前端和服务端之间传递的数据,绝大多数都用 JSON 承载。这时候 JSON 是"载体",而 HTTP 方法、状态码、URL 设计才是"协议"。第三种是数据存储和导出。MongoDB 这类 NoSQL 直接把文档存成类 JSON 结构,很多系统导出数据也喜欢导出成 JSON 文件,方便下游程序读取。还有不少播放器、阅读器会用 JSON 文件定义资源源、书源列表,本质上也属于"用 JSON 做配置和数据交换"的范畴。
这些场景有一个共同点:JSON 只是打包数据的容器,至于"拿到这包数据之后下一步该干嘛",需要另外的规则层来定义。这层规则,很多时候就是 JSON-RPC。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. JSON-RPC 是什么:在 JSON 之上约定的"对话规则"
2.1 RPC 的核心思想:像调本地函数一样调远程服务
RPC(Remote Procedure Call,远程过程调用)是一种非常古老的编程思想:把远程服务上的功能封装成本地函数一样去调用。你不需要关心网络细节,只需要传入方法名和参数,就知道最后能拿回一个结果。分布式系统早期用 CORBA、DCOM,后来在 Web 时代,XML-RPC 火了,再后来因为 XML 太啰嗦,JSON 又顺手,人们就把 JSON 和 RPC 结合,形成了 JSON-RPC。
JSON-RPC 不是某个大厂发明的私有协议,它有公开规范,目前主流版本是 2.0。规范定义得非常精简,核心就是规定请求对象和响应对象长什么样、用什么字段表示方法名和参数、出错时怎么表达、哪些请求需要响应、哪些不需要。正因为精简,它被大量嵌入到各种工具链中:编辑器间通信的语言服务器协议(LSP)就是基于 JSON-RPC 的,很多区块链节点的接口、IM 软件的内部指令通道也用它。
理解 RPC 的一个关键点是"面向动作"。REST 的思维是"资源",你操作一个 URL 对应的资源;JSON-RPC 的思维是"函数",你告诉对方执行哪个 method,带什么参数。同一个"计算两个数之和"的动作,REST 会设计成 GET /add?a=1&b=2 或 POST /math/add 这种资源路径,而 JSON-RPC 就是一个 method 字段里写 add。这种差异直接决定了选型方向。
2.2 JSON-RPC 2.0 的报文结构与必须遵守的字段
先看一个最标准的请求报文:
json复制{
"jsonrpc": "2.0",
"method": "subtract",
"params": [42, 23],
"id": 1
}
四个字段的含义分别是:jsonrpc 固定写 2.0,用来声明协议版本;method 是方法名,必须写字符串,对应服务端提前注册好的处理函数;params 是参数,可以是数组(位置参数)也可以是对象(命名参数);id 是这次调用的编号,用来把请求和响应配对。
标准的响应报文同样带 jsonrpc 和 id,成功时返回 result,失败时返回 error:
json复制{
"jsonrpc": "2.0",
"result": 19,
"id": 1
}
json复制{
"jsonrpc": "2.0",
"error": {
"code": -32601,
"message": "Method not found"
},
"id": "1"
}
注意响应里的 id 必须和请求里的 id 保持一致。为什么要多此一举?因为 JSON-RPC 允许服务端乱序返回。客户端发了 5 个请求,服务端哪个先处理完就先返回哪个,客户端靠 id 把结果对齐到对应的请求上。如果服务端处理某个请求时崩溃到根本拿不到 id,规范允许响应里的 id 设为 null。
2.3 通知、批量请求与错误码:协议里的隐藏细节
JSON-RPC 里有一种特殊的请求叫"通知"(notification)。它的特征是:没有 id 字段。客户端发一个没有 id 的请求,意味着"你执行就行,不用回我"。这非常适合日志上报、缓存预热这类不需要结果的操作。需要特别注意:服务端对通知绝对不能返回任何响应,否则客户端反而会困惑——因为客户端根本不知道哪个 id 对应哪条通知。
另一个细节是批量请求(batch)。客户端可以一次性发一个 JSON 数组,里面包含多条请求:
json复制[
{"jsonrpc": "2.0", "method": "add", "params": [1, 2], "id": 1},
{"jsonrpc": "2.0", "method": "get_time", "id": 2},
{"jsonrpc": "2.0", "method": "log", "params": ["hello"], "id": null}
]
服务端的响应也是一个数组,里面只包含需要响应的部分。上面第三条因为没有 id,是通知,所以响应数组里不会出现它。批量请求很适合"批量查询"场景,一次网络往返拿到所有结果,性能收益非常可观。
错误码方面,JSON-RPC 规范预留了一套标准码:
| 错误码 | 含义 | 说明 |
|---|---|---|
| -32700 | 解析错误 | 收到的文本连合法 JSON 都算不上 |
| -32600 | 无效请求 | 是合法 JSON,但不是合法的请求对象 |
| -32601 | 方法不存在 | method 没注册 |
| -32602 | 无效参数 | params 的类型或数量不符合方法定义 |
| -32603 | 内部错误 | 服务端执行方法时抛了未知异常 |
| -32000 到 -32099 | 服务端自定义错误 | 业务错误尽量用这个区间 |
很多新手把业务逻辑错误直接写成 -32603,这是偷懒也是不规范的做法。业务错误应该用 -32000 到 -32099 区间的负数自定义码,让调用方能区分"是框架层错误还是业务层错误"。
3. JSON 与 JSON-RPC 的核心差异与选型判断
3.1 一张表看清行为差异
我用一张对比表把两者的核心差异列出来,方便你收藏备用:
| 对比维度 | JSON | JSON-RPC |
|---|---|---|
| 本质 | 数据交换格式(语法层) | 远程调用协议(语义层 + 交互层) |
| 是否规定传输方式 | 不规定 | 不强制,但在 HTTP/WebSocket/TCP 上都能跑 |
| 是否规定请求结构 | 不规定,只要合法语法即可 | 强制要求 jsonrpc、method、id 等字段 |
| 是否规定错误格式 | 不规定,随便怎么表示 | 规定统一的 error 对象和错误码 |
| 是否要求有响应 | 不要求,它只是数据 | 普通请求要求,notification 不要求 |
| 是否支持批量 | 不支持,一份 JSON 就是一份数据 | 支持,数组形式的批量请求 |
| 典型应用 | 配置文件、数据存储、REST 载体 | 内部服务方法调用、LSP、指令通道 |
| 可读性 | 人可读,适合多种场景 | 同样可读,但字段冗余,更重"语义" |
核心一句话:一段 JSON-RPC 报文本身一定是一段合法 JSON,但一段合法 JSON 不一定是 JSON-RPC 报文。所以问"JSON 和 JSON-RPC 的区别",本质上是问"语法"和"协议"的区别。语法解决"怎么说",协议解决"说完怎么算完成、出错了怎么办"。
3.2 什么场景下该用 JSON,什么场景又该升级到 JSON-RPC
如果你只是存储配置、导出数据、传一个静态对象给对方,直接用 JSON 就够了,不需要引入 JSON-RPC 的概念。比如 package.json、系统导出文件,它们就是"数据",没有"调用"这回事。
如果你在做公开的 CRUD API,需要面向资源模型、需要利用 HTTP 缓存、需要让调用方通过 URL 就能猜出业务边界,优先考虑 REST,用 JSON 作为请求和响应的载体。REST 的成功之处在于 HTTP 状态码语义清晰(200 成功、404 不存在、422 参数错误),对外部开发者更友好。
如果你在做内部服务之间的动作调用,尤其是方法多、动作密集、双向通信低频请求高频的场景,JSON-RPC 更顺手。典型例子:
- 语言服务器协议(LSP):编辑器把"跳转定义""自动补全"这类动作封装成 method,发给语言服务端,两者用 JSON-RPC 通信。
- 网关指令下发:控制端下发"重启某服务""取某指标",method 加 params 的表达比 REST 的路径更直接。
- 需要服务端主动推送结果的场景:客户端发一个长耗时任务请求,服务端完成后主动再推一条响应,配合 notification 和 id 设计,比 REST 轮询舒服得多。
选型不是"谁替代谁",而是"谁更匹配这个场景"。我见过不少团队在对外 API 上硬上 JSON-RPC,结果调用方要查一堆方法名和参数文档,体验很差;也见过内部脚本里强行用 REST 模拟动作调用,URL 越写越奇怪。这两种方向都值得警惕。
4. 实操演示:从 JSON 数据加工到 JSON-RPC 服务落地
4.1 先把 JSON 序列化和反序列化跑通
无论你用 JSON 还是 JSON-RPC,第一步都是把 JSON 读写跑通。用 Python 写一遍最基础的操作:
python复制import json
data = {
"name": "demo",
"tags": ["json", "rpc"],
"enabled": True,
"timeout": 30
}
# 序列化:对象 -> JSON 字符串
text = json.dumps(data, ensure_ascii=False, indent=2)
print(text)
# 反序列化:JSON 字符串 -> 对象
restored = json.loads(text)
print(restored["tags"][0])
ensure_ascii=False 是我的习惯,否则中文会被转成 \uXXXX,虽然合法但很难读。至于"JSON 用什么打开"这种问题,现代编辑器像 VS Code、NotePad++ 都能直接打开,想要离线格式化或校验,可以用 Python 自带的命令行工具:
bash复制python -m json.tool response.json
这个命令会输出格式化后的 JSON,如果语法错误会直接报错,非常适合临时校验一个文件是不是合法 JSON。
数据处理环节里,最让我头疼的一直是日期类型。下面的代码一定会踩:
python复制import json
from datetime import datetime
payload = {"now": datetime.now()}
text = json.dumps(payload) # 报错:Object of type datetime is not JSON serializable
解决办法是写一个默认的转换函数,把 datetime 转成 ISO 8601 字符串:
python复制def json_default(obj):
if isinstance(obj, datetime):
return obj.isoformat()
raise TypeError(f"Type {type(obj)} not serializable")
text = json.dumps(payload, default=json_default)
print(text)
这个小工具函数建议沉淀到项目公共模块里,因为后面所有接口联调都会用到。
4.2 用 Python 标准库构建一个 JSON-RPC 服务端
理解了 JSON 的基础操作,就可以搭 JSON-RPC 服务端。我用标准库的 http.server 写一个最小实现,故意不引入 Flask 这类重量级框架,就是为了让你看清协议本身长什么样:
python复制import json
from http.server import BaseHTTPRequestHandler, HTTPServer
handlers = {}
def register(name):
def decorator(fn):
handlers[name] = fn
return fn
return decorator
@register("add")
def add(a, b):
return a + b
@register("get_time")
def get_time():
from datetime import datetime
# 返回 ISO 字符串,避免 JSON 没有日期类型带来的序列化问题
return datetime.now().isoformat()
def dispatch(req):
# 单条请求分发,批量请求这里省略
method = req.get("method")
params = req.get("params", [])
handler = handlers.get(method)
if handler is None:
return {
"jsonrpc": "2.0",
"error": {"code": -32601, "message": "Method not found"},
"id": req.get("id")
}
try:
if isinstance(params, list):
result = handler(*params)
else:
result = handler(**params)
return {"jsonrpc": "2.0", "result": result, "id": req.get("id")}
except TypeError as exc:
return {
"jsonrpc": "2.0",
"error": {"code": -32602, "message": f"Invalid params: {exc}"},
"id": req.get("id")
}
except Exception as exc:
return {
"jsonrpc": "2.0",
"error": {"code": -32603, "message": str(exc)},
"id": req.get("id")
}
class Handler(BaseHTTPRequestHandler):
def do_POST(self):
length = int(self.headers.get("Content-Length", 0))
raw = self.rfile.read(length)
try:
req = json.loads(raw)
except json.JSONDecodeError as exc:
resp = {"jsonrpc": "2.0", "error": {"code": -32700, "message": f"Parse error: {exc}"}, "id": None}
else:
# notification 不返回响应
if "id" not in req:
self.send_response(204)
self.end_headers()
return
resp = dispatch(req)
body = json.dumps(resp).encode("utf-8")
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
if __name__ == "__main__":
HTTPServer(("127.0.0.1", 8080), Handler).serve_forever()
这里的 dispatch 函数就是协议的核心:查找 method、区分位置参数和命名参数、把不同异常映射到不同的 JSON-RPC 错误码。如果方法不存在,返回 -32601;如果参数不匹配,返回 -32602;如果是未知异常,返回 -32603。这套错误码映射是一个合格 JSON-RPC 服务端必须具备的"礼貌"。
4.3 客户端调用与 id 关联:乱序响应怎么处理
客户端我用 requests 来演示,因为真实项目里这样调用最直观:
python复制import json
import requests
def rpc_call(url, method, params=None, rpc_id=1):
payload = {
"jsonrpc": "2.0",
"method": method,
"params": params or [],
"id": rpc_id
}
resp = requests.post(url, json=payload, timeout=5)
result = resp.json()
if "error" in result:
raise RuntimeError(f"RPC error {result['error']['code']}: {result['error']['message']}")
return result["result"]
print(rpc_call("http://127.0.0.1:8080", "add", [3, 4], rpc_id=1))
print(rpc_call("http://127.0.0.1:8080", "get_time", rpc_id=2))
客户端拿到响应后,第一件事是检查有没有 error 字段,再取 result。不过上面这段代码有个隐患:如果同时发多个请求,服务端乱序返回,靠什么匹配?答案是 id。所以更严谨的客户端应该用 id 建立映射:
python复制def rpc_call_batch(url, calls):
# calls: [(method, params, rpc_id), ...]
batch = [
{"jsonrpc": "2.0", "method": m, "params": p, "id": i}
for m, p, i in calls
]
resp = requests.post(url, json=batch, timeout=10)
bodies = resp.json()
result_map = {item["id"]: item for item in bodies}
return result_map
整体思路是:发出批量请求后,收到的响应不一定按请求顺序排列,但每个响应里的 id 会明确告诉你它对应哪一次调用。客户端把响应按 id 装到字典里,后面业务代码按 id 取结果就行。这个模式在消费端非常常见,一定要养成按 id 聚合而不是按数组下标取结果的习惯。
再看一眼 REST 和 JSON-RPC 在同一个动作上的报文差异,你会更直观:
REST 风格:
http复制POST /math/add HTTP/1.1
Content-Type: application/json
{"a": 1, "b": 2}
http复制HTTP/1.1 200 OK
Content-Type: application/json
{"result": 3}
JSON-RPC 风格:
http复制POST /rpc HTTP/1.1
Content-Type: application/json
{"jsonrpc": "2.0", "method": "add", "params": {"a": 1, "b": 2}, "id": 1}
http复制HTTP/1.1 200 OK
Content-Type: application/json
{"jsonrpc": "2.0", "result": 3, "id": 1}
对比之后你会发现,REST 把动作的含义放在 URL 和路径上,JSON-RPC 把动作的含义放在 method 字段里;REST 的状态码承载语义,JSON-RPC 的语义全部收敛在 error 对象里。两者都是合法的 JSON 载体,但"对话规则"完全不同。
5. 高频报错排查:JSON 与 JSON-RPC 实战避坑
5.1 日期反序列化失败:JSON 没有 Date 类型,框架却要类型
这个报错原文是 json parse error: cannot deserialize value of type java.util.Date from String,几乎每个用 Jackson 的 Java 后端都见过。根因相当典型:JSON 里没有日期类型,所以日期在 JSON 里只能表现为字符串或数字;但服务端 DTO 的字段类型是 java.util.Date,Jackson 收到字符串后不知道该按什么格式转换,于是直接抛异常。
解决办法有几条路径。最简单的是给字段加注解指定格式:
java复制@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;
如果你用的是 java.time.LocalDateTime,需要额外注册 JavaTimeModule,只加注解往往不够。更稳妥、也更推荐的方式是:统一约定用 ISO 8601 格式字符串传日期,比如 2026-01-01T10:00:00Z,天然可读、无时区歧义,服务端拿到后按 Instant.parse 或 OffsetDateTime.parse 处理。
这个报错的本质粗看是"框架配置问题",细想其实还是"格式无日期,但契约需要日期"的映射问题。所以团队在做接口规范时,必须在文档里明确日期到底用字符串、时间戳还是自定义对象表示,否则 JSON 层永远不会替你做决定。
5.2 "No start of JSON char found":响应体根本不是 JSON
module result deserialization failed: no start of JSON char found 这句话在 Ansible 这类工具里很常见。结合上下文看,是工具尝试把某段文本解析成 JSON,结果发现第一个有效字符不是 { 或 [,也就是说你喂给它的东西根本不是 JSON。
常见原因有五种:
- 服务端返回的是 HTML 错误页,比如网关 502 页面,开头是
<!DOCTYPE html>。 - 文件带了 UTF-8 BOM 头,
json.loads会直接不认。 - 返回内容混入了日志或警告文本,比如常见的
Warning: xxx出现在 JSON 前面。 - 响应体是空字符串或纯空白。
- 目标 URL 实际指向的是一个二进制文件,比如压缩包或图片,被误当成 JSON 读取。
排查思路其实就一句话:先看原始内容,别急着猜。用 curl 把原始响应抓下来,看前 500 个字节:
bash复制curl -i http://target/api | head -c 500
如果开头有 feff,说明是 BOM,Python 里用 utf-8-sig 编码读就能解决;如果开头是 <,基本可以断定拿到了 HTML 页,这时应该检查 URL、Header 或认证信息。如果响应内容混合了日志,需要去服务端把日志输出重定向到标准错误,别让它污染标准输出。这些坑不只在 Ansible 里出现,任何用脚本解析接口响应时都会碰到。
5.3 422 Failed to deserialize the JSON body:请求体与服务端契约不一致
Unexpected status 422 unprocessable entity: failed to deserialize the JSON body 常见于 FastAPI 和 Spring 这类校验严格的框架。它表示:你发的确实是 JSON,但"形状"不符合服务端期待的类型或字段约束。
排查步骤按优先级排:
- 打开服务端日志,找到具体的错误路径和字段名。FastAPI 会把具体校验失败的位置写在响应里,比如
body -> a -> value is not a valid integer。 - 对照服务端 DTO 的字段名,注意多传了字段、少传了字段、字段类型不匹配这三种情形。
- 特别注意日期字段和枚举字段。日期字符串格式对不上是最常见的原因,枚举传了服务端没定义的值也一样。
- 检查
Content-Type是否确实是application/json。有些客户端会把 JSON 文本以text/plain发送,服务端直接拒绝。 - 先用离线格式化工具或
python -m json.tool校验 JSON 本身合法,排除语法层错误。
这个问题在 REST 和 JSON-RPC 下都会出现,因为它们的载体都是 JSON。核心认知是:JSON 语法合法,不等于满足业务契约;框架的反序列化器会根据你的 DTO 定义逐字段比对,任何不一致都会暴露成这类 4xx 或 5xx 错误。
5.4 JSON-RPC 的专属坑:我踩过之后才明白的细节
光看规范容易忽略,落到代码里才会碰到这些特殊问题。
第一个坑是批量请求的返回格式。规范规定:如果请求是数组,响应也应该是数组;如果请求是单个对象,响应就是单个对象。但有些服务端实现偷懒,永远返回数组,或者永远返回对象。客户端解析时一定要先判断 resp.json() 的类型,再决定走单条解析还是批量解析。
第二个坑是 notification 的响应。服务端收到没有 id 的请求时,不应该返回任何内容。但不少新手实现里直接走统一返回逻辑,导致客户端的普通请求和通知混在一起,响应数组里多出几个没法配对的条目,客户端只能干瞪眼。
第三个坑是 id 的类型一致性。请求里 id 是数字 1,响应里变成字符串 "1",虽然视觉上"差不多",但严格匹配的客户端会直接丢到这个结果。所以服务端在返回 id 时,最好原样拷贝请求的 id,不要做任何类型转换。
第四个坑是错误码使用不规范。很多团队把业务逻辑异常也写成 -32603,调用方想针对"库存不足"单独做重试都无从下手。正确做法是业务错误用 -32000 到 -32099 区间自定义,框架错误才用标准码。另外,服务端最好统一返回 HTTP 200,把失败细节放在 JSON-RPC 的 error 对象里,否则有些 HTTP 客户端会在拿到非 2xx 状态码时直接抛异常,根本不给你看响应体的机会。
5.5 调试工具清单:命令行和抓包两手抓
最后给一套我日常用的调试工具,全部贴合 JSON 和 JSON-RPC 的排查需求:
| 工具 | 用途 | 我的使用习惯 |
|---|---|---|
| jq | 命令行查询 JSON 字段 | `curl -s http://... |
| python -m json.tool | 离线格式化和语法校验 | python -m json.tool bad.json |
| VS Code 内置格式化 | 编辑器里快速整理 | 打开 JSON 文件后 Shift+Alt+F |
| curl | 直接测 JSON-RPC 接口 | 带 -H 'Content-Type: application/json' -d '...' |
| Postman / APIFox | 构造请求、保存接口用例 | 适合团队协作,方便回放 |
| Burp Suite | 抓包重放、接口安全测试 | 看完整请求响应,适合排查隐藏问题 |
jq 是我最依赖的命令行工具,比如快速取 JSON-RPC 响应里的 result 字段:
bash复制curl -s http://127.0.0.1:8080 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"add","params":[1,2],"id":1}' \
| jq -r '.result'
输出直接是 3,不需要任何多余处理。另外如果你在用 Kettle(PDI)做数据管道,它同样支持解析 JSON:用 JSON Input 步骤读取 JSON 字段,用 JSON Output 生成 JSON 结构,处理 REST 接口返回或配置文件都很方便。只是注意 Kettle 里的 JSON path 写法跟 jq 不完全一样,别把表达式直接搬过去用。
在我自己的项目实践里,最值得沉淀的经验其实就一条:写代码前先写清楚"通信契约"——字段、类型、日期格式、错误码区间。JSON 只负责让数据长成标准模样,JSON-RPC 只负责约定怎么把请求送过去、怎么把结果拿回来,真正让系统稳定的,永远是人在中间补充的约定文档和边界测试。把这层想明白,绝大多数格式解析和协议报错都能少踩一半。
