JSON-RPC 2.0 规范解读
概述
JSON-RPC是一种无状态、轻量的远程调用(RPC)协议,其有一套规范,定义了若干数据结构及其处理规则。它对传输层是无关的,可以在同一进程中传递使用,也可以跨进程、跨环境如socket、http等传递环境使用。
官方对JSON-RPC 2.0的规范定义其实也非常简单,主要请求对象、响应对象。
请求对象
一个RPC调用是通过向服务器发送一个Request对象来表示。
{
"jsonrpc": "2.0",
"method": "xxx",
"params": ...,
"id": ...
}
Rrequest对象具体有以下成员:
- jsonrpc:一个指定JSON-RPC协议版本的字符串,必须正好是”2.0″。该成员必须。
- method:一个包含要调用的方法名称字符串,用于区分要调用的是那个方法。该成员必须。
- params:一个结构化智,用于存储方法调用期间要使用的参数值。该成员可省略。
- id:由客户端建立的标识符,如果包含必须是一个字符串、数字或NULL值。
可以理解为method就是函数名,params就是函数的参数。
params是可以省略的,id的如果请求需要回复是不必须要的,如果请求不需要回复就不需要带,这种一般是通知。
通知
request对象可以分为带回复的请求,和不需要回复的请求,后者成为notification,跟BLE的传输协议是不是有点像?(没错就是notify和indicate的区别)。
(1)请求需要回复的示例
{
"jsonrpc": "2.0",
"id": 1,
"method": "get_temp",
"params": {}
}
(2)通知(请求不需要回复)
{
"jsonrpc": "2.0",
"method": "robot.chassis.forward",
"params": { "speed": 0.5 }
}
通知是一个不需要id成员的请求对象,一个作为通知的请求对象表示客户端对相应的响应对象都不感兴趣,因此不需要向客户端返回响应对象。服务器必须不回复通知,包括哪些在批量请求中的通知。
根据定义,通知是不可确认的,因为它们没有要返回的响应对象。因此,客户端不会知道任何错误(例如无效的参数、内部错误)。
参数结构
参数是可选的,如果存在参数,rpc调用的参数必须以结构化值的形式提供。可以通过array数组按位置提供或通过object对象按名称提供。
- 按数组位置提供: params必须是一个数组,其中包含服务器期望的顺序值。
- 按对象名称提供:params必须是一个对象,其成员名称必须与服务器期望的参数名称匹配。缺少预期名称可能会导致生成错误。名称必须完全匹配,包括大小写,以匹配方法期望的参数。
(1)按数组位置
"params": [value1, value2, value3]
参数顺序必须完全匹配,客户端与服务器双方必须清楚位置含义,如果顺序错了就调用失败(服务器无法解析),参数数目要匹配缺少或多给都会报错。如下:
{
"jsonrpc": "2.0",
"method": "add",
"params": [3, 5],
"id": 1
}
这种一般适用于资源小,带宽紧张的事实控制,参数稍而且固定的场景。
(2)按对象名称
"params": { "x": 1, "y": 2, "speed": 0.5 }
按对象名称提供的最灵活,最容易读,这是一个键-值对的方式,但是参数的名称必须要完全匹配(包括大小写),参数的顺序没有要求。如下示例:
{
"jsonrpc": "2.0",
"method": "robot.arm.move",
"params": {
"joint1": 10,
"joint2": -5
},
"id": 1
}
按数组的方式可以理解为只有原始数据,没有对应数据的标签。而按照对象的方式有数据和对应的标签。
响应对象
当发起一个rpc调用时,除了通知外,服务器都需要进行响应回复。响应分为正常的响应和错误的响应。
成功的响应,格式如下:
{
"jsonrpc": "2.0",
"result": ...,
"id": ...
}
错误的响应:
{
"jsonrpc": "2.0",
"error": {
"code": ...,
"message": ....
},
"id": ...
}
响应表示为一个JSON对象,包含一下成员:
- jsonrpc:一个指定JSON-RPC协议版本的字符串,必须正好是2.0。
- result:成功的响应必须包含。
- error:错误的响应必须包含。
- id: 该成员必须,必须与请求对象的id成员值相同,表示一一对应。
成功响应
成功响应回复的是result的成员,示例如下:
{
"id": 2,
"jsonrpc": "2.0",
"result": {
"temperature": 3,
"desc": "Sunny"
}
}
错误响应
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32602,
"message": "Invalid params",
"data": "speed must be 0~1"
}
}
错误的响应官方提供了标准的错误码含义。
| code | 含义 |
|---|---|
| -32700 | Parse error 解析错误 |
| -32600 | Invalid Request 无效请求 |
| -32601 | Method not found 找不到方法 |
| -32602 | Invalid params 参数错误 |
| -32603 | Internal error 内部错误 |
| -32000 to -32099 | 保留用于实现定义的服务器错误。 |
批量调用
同时发送多个 Request 对象时,客户端可以发送一个包含 Request 对象的数组。服务器在处理完所有批量的request对象后,应返回一个包含相应response对象的数组。每个request对象都应该有一个对象的response对象,但通知不应有任何response对象。服务器可以将批量rpc调用作为一组并发处理,以任意顺序和任意并行度进行处理。
从批量调用返回的response对象可以在数组中按任意顺序返回。客户端应根据每个对象的id成员,在request对象集和结果response对象集之间匹配上下文。
如果批量rpc调用本身无法被识别为有效的JSON或至少包含一个值的数组,服务器的响应必须是一个单独的response对象。如果发送给客户端response数组中不包含任何response对象,服务器不得返回空数组,而应什么也不返回。
以下是示例一次发多个请求:
[
{ "jsonrpc": "2.0", "id": 1, "method": "get_temp" },
{ "jsonrpc": "2.0", "id": 2, "method": "get_humidity" }
]
响应:
[
{ "jsonrpc": "2.0", "id": 1, "result": 25 },
{ "jsonrpc": "2.0", "id": 2, "result": 60 }
]
这种批量的调用一般在多传感器融合的时候非常好用。