常见问题

TcaplusDB PB表 RESTFul API 常见问题

1. restproxy响应消息中的http code用法

2.0 的响应映射看清楚了。接下来看 tcaprestproxy_http_msgproc.cpp 里的 93 处(包括请求入口、超时处理和 1.0 的响应处理),先列出每处所在的函数。

2.0 部分已经清楚。再看 1.0 的 ProcResponseFromTcaproxy,它的 500 用得多(55 处),要确认和 2.0 的差别在哪里。

1.0 的映射也清楚了。最后看一下 tcaproxyd 里的 10 处使用,判断要不要写进文档。

再补两处细节:2.0 请求阶段发送到 tcaproxy 失败时的返回值,以及超时配置项的名字。

下面是可以直接贴进文档的 restproxy HTTP 状态码说明,按当前代码里 6 个宏的全部用法整理(1.0 和 2.0 都覆盖)。最后附了梳理时发现的几处异常,其中第 1 条可能是鉴权漏洞,建议优先确认。


HTTP 状态码

restproxy 的所有响应体都是 JSON,都带 ErrorCode 和 ErrorMsg 两个字段,ErrorCode = 0 表示成功。

HTTP 状态码只用来区分错误大类,判断请求是否成功要看 ErrorCode:返回 200 时 ErrorCode 也可能不为 0。

状态码含义
200请求处理完成。ErrorCode = 0 表示成功;2.0 协议下有一部分错误也用 200 返回
400请求在 restproxy 阶段被拒绝或处理失败;或存储层返回了和请求内容相关的错误
401签名(密码 MD5)校验失败
404记录或索引不存在
408请求在 restproxy 中等待超时,或因为表结构变更、服务重新初始化被中止
500存储层返回其他错误,或 restproxy 内部错误

200 OK

  • 成功:ErrorCode = 0,ErrorMsg = "Succeed"。
  • 查询错误码描述(GetErrMsg):固定返回 200,ErrorCode 和 ErrorMsg 是被查询的错误码及其描述。
  • 仅 2.0 协议:下面这些情况返回 200,但 ErrorCode 不为 0。
    • 存储层返回成功,但 restproxy 处理结果时出错。比如批量、遍历、索引查询里有部分记录处理失败,这时成功的记录仍然在 MultiRecords 里返回;又比如记录转 JSON 失败(-2579)。
    • 响应体超过 9950000 字节:ErrorCode = -13075,不返回记录内容。
    • 索引查询(IndexQuery)没有匹配的记录:ErrorCode = 261。
    • Traverse / ListTraverse 的请求体参数错误,比如缺少 Limit、Limit 小于等于 0、TraverseSession(旧版本为 TraveseSession)格式错误:ErrorCode = -1811。

400 Bad Request

  1. 请求阶段失败(restproxy 收到请求后、转发到存储层之前):
    • HTTP 报文无法解析:-2579,ErrorMsg 为 “request format error”。
    • 请求体超过 9950000 字节:-13075。
    • 缺少 x-tcaplus-target:-11539;target 不支持:-2067。
    • 表不存在:-12819。注意这里返回的是 400,不是 404。
    • 其他请求头缺失或非法、请求体不是合法 JSON、字段校验失败、缺少必填字段等:一般是 -1811。
    • restproxy 构造请求失败或发送请求失败:也返回 400。ErrorCode 为具体错误码,ErrorMsg 一般是 “Internal Server Error” 或 “Send … Request Error”。
  2. 存储层返回下面这些错误码时:
    • -7949:数据版本号校验失败。Replace、List 写操作、FieldUpdate、FieldInc 会同时返回当前的 RecordVersion。
    • -1293:记录已存在。
    • -3341:自增或自减溢出。
    • -3853:下标越界。
  3. 仅 1.0 协议:批量或遍历类操作有部分记录获取失败(-275)、字段类型不支持。同样的情况在 2.0 协议下返回 200。

401 Unauthorized

签名校验失败时返回,ErrorCode = -279,ErrorMsg = "Illegal password MD5"。1.0 和 2.0 行为一致。触发条件:

  • x-tcaplus-pwd-md5 和业务密码的 MD5 不一致;
  • 没带 x-tcaplus-pwd-md5,而 x-tcaplus-username 和 x-tcaplus-username-pwd-md5 只带了其中一个,或者有一个是空值。

如果用的是用户名加密码的方式(不带 x-tcaplus-pwd-md5),校验由存储层完成。这时校验失败返回的是 500,ErrorCode 为存储层的错误码,不是 401。

404 Not Found

  • 存储层返回 261:记录不存在。
  • 存储层返回 -34565:索引不存在。
  • 仅 1.0 协议:IndexQuery 没有匹配的记录(ErrorCode = 261)。2.0 协议下这种情况返回 200。

408 Request Timeout

ErrorCode = -525,ErrorMsg = "request timeout"。触发场景:

  • 请求发到存储层后,超过 restproxy 配置的会话超时时间(TcapRestProxyCfg.SessionTimeout,默认 3000 ms)还没收到响应。超时是周期性扫描检查的,实际返回 408 的时间会比配置值稍晚。
  • 表结构变更或表被删除时,这张表上所有还没完成的请求。
  • restproxy 重新初始化存储访问时,所有还没完成的请求。

500 Internal Server Error

  • 存储层返回了上面没列出的非 0 错误码。ErrorCode 为原始错误码,ErrorMsg 为对应的错误描述,比如 Condition 非法、鉴权失败等。
  • 存储层响应里缺少表名(-12051),或者响应的 app、zone、table 和请求不一致(-12819,ErrorMsg 为 “App Or Zone Or Table Not Match”)。
  • GetRecordTotal 读取记录总数失败。
  • restproxy 构造 HTTP 响应失败:-2323,ErrorMsg 为 “build response failed”。
  • restproxy 的存储访问模块没有初始化成功:-256,ErrorMsg 为 “Tcaprestproxy Api Not Init”。
  • 仅 1.0 协议:存储层返回成功,但 restproxy 处理结果时出错,比如取数据、反序列化、转 JSON 失败。同样的情况在 2.0 协议下返回 200。

1.0 和 2.0 的差异汇总

场景1.02.0
存储层成功,但 restproxy 处理结果出错500200,ErrorCode 不为 0
批量或遍历有部分记录失败400200,ErrorCode 不为 0
IndexQuery 没有匹配记录404200,ErrorCode = 261
Traverse 请求体参数错误400200,ErrorCode = -1811

2. x-tcaplus-hide-default-primitive用法

作用:控制返回的 JSON 记录里是否省略“未设置”的字段。

适用协议:仅 RESTful 2.0(请求头 x-tcaplus-protocol-version: 2.0)。1.0 协议会忽略这个请求头。

取值

  • true(不区分大小写):开启。返回的记录里不包含“未设置”的非 repeated 字段。
  • 不传,或取其他值:关闭(默认)。按表结构输出全部字段,未设置的字段用默认值填充:
    • 数值为 0,字符串为 "",bool 为 false,枚举为默认枚举值;
    • 子消息展开成一个由默认值组成的对象。

影响范围

  • 只影响 restproxy 返回的记录内容,包括 Record、MultiRecords[].Record、MoreKeys。
  • 不影响写入、Condition 过滤和存储的数据。
  • 所有会返回记录的 2.0 接口都生效:
    • GetRecord、BatchGetRecord、GetRecordByPartKey;
    • FieldGet、BatchFieldGet;
    • Traverse、ListTraverse;
    • IndexQuery(非聚合查询)、List 系列读接口;
    • 通过 x-tcaplus-result-flag 返回记录的写接口。
  • 不影响 GetRecordTotal 和聚合类 IndexQuery 的结果。

开启后,“未设置”的判断规则

字段类型proto2 表proto3 表
非 repeated 标量(数值、bool、枚举、string、bytes)没写入过就省略;显式写入的值即使等于默认值,也会返回值等于默认值(0、""、false、编号为 0 的枚举值)就省略,不管是否显式写入过
子消息没写入过就省略没写入过就省略;写入过(包括写入 {})就返回
repeated、map始终返回,空时为 []同左
oneof 成员只返回被设置的那个成员(和开关无关)同左;被设置的成员即使值为 0 也会返回

注意事项

  1. 规则对嵌套子消息逐层生效。
  2. 和 Select 一起用时,选中的字段如果属于“未设置”,同样不会出现在响应里。
  3. proto3 表的主键字段也适用这条规则:主键值为 0 或空串时会被省略。
  4. 当前版本不支持 proto3 的 optional 关键字。所以在 proto3 表里,标量字段“没写入”和“写入了默认值”是区分不出来的,开启后两者都会被省略。

示例(proto3)

message Inner { int32 in_v1 = 1; string in_v2 = 2; }
message Pay   { int64 total_money = 1; int32 pay_times = 2; Inner msg_in = 3; repeated int32 tags = 4; }

写入 "pay": {"total_money": 1, "pay_times": 0} 后读取:

  • 不带请求头(默认):
    "pay": {"total_money": 1, "pay_times": 0, "msg_in": {"in_v1": 0, "in_v2": ""}, "tags": []}
    
  • 带 x-tcaplus-hide-default-primitive: true:
    "pay": {"total_money": 1, "tags": []}
    

7. 常见问题

使用RESTFul API的过程中,有疑问或者遇到报错,可参考“常见问题”和“错误码含义和处理方法”进行处理。