2. 密文检索接口简介:RESTful 接口设计、接口参数、返回格式解析
做接口自动化测试,第一件事就是搞清楚被测接口长什么样。
密文检索接口说白了就是一个 RESTful API。它接收加密后的查询词,返回匹配的密文结果。你不需要关心内部怎么加密、怎么建索引,但接口的请求格式、参数约束、响应结构,你得门儿清。
我最早接触这类接口时,以为跟普通搜索接口差不多,传个keyword就完事了。结果一调返回400——人家要的是Base64编码后的密文,我没处理。从那以后,我再也不敢跳过接口规范文档。
2.1 RESTful 接口设计原则
密文检索接口遵循典型的 REST 风格,但也有些特殊之处。我把它拆成几个关键点:
- 资源路径:通常是一个端点,例如
/api/v1/ciphertext/search,动词用 HTTP 方法表达。 - 方法选择:查询类接口一律用
POST,因为请求体携带密文,不适合暴露在URL中。 - 无状态:每次请求携带全部必要信息,不依赖服务端会话。
- 版本控制:路径中含
v1,方便后续升级。
另外,REST 不是死教条。你可能会看到接口设计成 /api/search 不带版本号,或者用 /ciphertext/search 这样的短路径。没关系,只要团队约定一致就行。但自动化测试脚本需要跟着实际路径走,千万别想当然。
2.2 接口参数解析
密文检索接口的参数分为三大部分:
| 参数类别 | 参数名 | 类型 | 说明 |
|---|---|---|---|
| 请求头 | Content-Type |
string | 固定为 application/json |
| 请求头 | Authorization |
string | Bearer ,用于身份校验 |
| 请求体 | ciphertext |
string | Base64 编码的加密查询词 |
| 请求体 | topK |
integer | 返回前 K 条最相似结果,默认 10 |
| 请求体 | threshold |
float | 相似度阈值,低于该值的结果不返回 |
| 请求体 | page, size |
integer | 分页参数(部分接口支持) |
threshold 这个参数,不同服务的取值范围不一样。有的是 0~1,有的是 0~100。我曾经没仔细看文档,传了个 0.8,结果服务端当作 0.8% 处理,返回了一堆垃圾结果。一定要确认文档中阈值的单位。
一个典型的请求体 JSON 示例:
{
"ciphertext": "U2FtcGxlRW5jcnlwdGVkVGV4dA==",
"topK": 5,
"threshold": 0.85
}
嗯,这里要注意:ciphertext 字段的值必须是 Base64 编码后的字符串,直接传 raw bytes 会报 422。我在项目中遇到过新同事把加密后的二进制扔进去,连报错信息都看不懂。建议在测试脚本里先做一次 Base64 编码校验。
2.3 返回格式解析
接口返回统一采用 JSON 格式。标准结构长这样:
{
"code": 0,
"message": "success",
"data": {
"total": 3,
"results": [
{
"id": "doc_001",
"ciphertext": "RW5jcnlwdGVk....",
"score": 0.9234
},
{
"id": "doc_042",
"ciphertext": "RW5jcnlwdGVk....",
"score": 0.8761
}
]
}
}
字段含义非常直观:
- code:业务状态码,0表示成功,非0表示各类错误。
- message:描述信息,可用于日志或用户提示。
- data.total:匹配的总记录数,用于分页统计。
- data.results:结果列表,每个元素包含文档ID、密文字段、相似度分数。
注意:返回的密文是完整的加密文档内容,不是查询词。也就是说,你拿这个密文去解密,得到的是原始文档,而不是你的查询关键词。
错误情况下的返回格式略有不同。比如参数校验失败:
{
"code": 40001,
"message": "ciphertext must be valid base64 string",
"data": null
}
我在做自动化测试时,会专门写一个断言函数,先校验 code 字段是否符合预期,再根据 code 决定要不要解析 data。如果 code != 0 却强解 data,测试会直接挂掉。这个逻辑一定要进脚本。
2.4 核心流程概览
把上面的知识点串起来,整个密文检索请求的生命周期如下图所示。画这张图的时候我特意把“构造参数”和“解析响应”分开,因为测试脚本里这两个步骤最容易出错。
resp = requests.post(url, json=payload, headers=headers),然后 resp.json() 拿到数据。不过记得加上超时和重试逻辑,网络波动的时候很有用。