HTTP 错误码就是文档:从 404/405/400 反推一个没文档的接口

举报
yd_255011313 发表于 2026/11/01 14:45:45 2026/11/01
【摘要】 华为云控制台自动化三层鉴权排障:MFA 提示页路由循环、SSO TGC 单次消耗、KooLabs 联邦登录与 auth-token 鉴权。

对着没有 OpenAPI 文档的私有接口调参,最怕的不是报错,而是报错信息太少。但这段时间发现,**错误码本身携带的信息量比想象的大得多**——只要你会读,它能把接口契约一块一块吐出来。

## 一、404 和 405 的区别,决定了你下一步改什么

给一个云实验平台写调度脚本时,我需要找到"检查实验状态"的端点。猜了三轮:

```
GET /exercise/2325/check-operation-status → 404 Not Found
GET /exercise/check-operation-status?experiment_id=2325 → 405 Method Not Allowed
GET /exercise/2325/status → 404 Not Found
```

这三条结果看着都像失败,其实信息完全不同:

- **404 = 路径不存在**。`/exercise/2325/check-operation-status` 这个 URL 结构是错的,`experiment_id` 不在 path 里。
- **405 = 路径存在,方法不对**。第二个请求打在正确的端点上,但服务端只接受 POST,不接受 GET。

所以看到 405 应该做的事是**换方法**,不是换路径。我改成 `POST /exercise/check-operation-status?experiment_id=2325`,一次就通。

这个判断顺序能省很多时间:**先 405 再 404**。405 说明你摸到了门口,404 说明你还在墙外。

## 二、400 里的措辞差异,比错误码本身有用

同样是 400,措辞不一样,指向的问题也不同。结束实验这一步我卡了很久:

```
POST /exercise/end {"experiment_id": 2325, "doneScene": 2}
→ 400 Invalid param error; doneScene 不能为空

POST /exercise/end?experiment_id=2325&doneScene=2
→ 400 Invalid param error, cannot convert the request
```

第一个说「**不能为空**」——我明明传了 `doneScene` 为什么说空?真相是**字段名不对**。服务端要的是 `done_scene`(下划线),`doneScene` 它压根没认出来,所以认为这个必填项缺失。

第二个说「**无法转换**」——"conversion"这个词是关键。它意味着参数**位置放错了**,服务端在 query 里找不到它需要的东西,类型转换失败。正确的做法是放回 body。

所以这套措辞可以这样读:

| 报错措辞 | 真实含义 | 怎么改 |
|---|---|---|
| 不能为空 / required | **字段名错**(或真缺) | 换命名风格:驼峰 ↔ 下划线 |
| 无法转换 / cannot convert | **参数位置错** | body ↔ query 互换 |
| 类型不匹配 / type not match | 值的类型不对 | 数字 vs 字符串 |

我一开始照着「不能为空」去补参数,补了十几次都没用。换成 `done_scene` 立刻 200,返回 `{"result": null}`。

**报错信息里的字段名是后端框架自动转出来的驼峰形式,不是它真正接收的键名。** 这个坑很隐蔽。

## 三、业务错误码是一张地图

有些平台不用标准 HTTP 状态码表意,而是统一返回 200 + 自定义 `error_code`。这时候错误码表就是唯一的地图。我摸到的几个:

| 错误码 | 含义 | 处置 |
|---|---|---|
| `HD.31400001` + `Not Found` | 路径错 | 换路径 |
| `HD.31400001` + `Method Not Allowed` | 方法错 | GET 换 POST |
| `HD.31420014` | `reduce lab points fail` | 扣虚拟货币失败(余额不足) |
| `HD.31420023` | `lab instance not exist or has end` | 会话不存在/已回收 |
| `HD.314710026` | `cannot binding lab instance` | 实例还没分配到 |

`HD.31420014` 这条最值得说。字面是"扣点数失败",我第一反应是"余额不够",但同一时刻另一个接口返回 `remainUserTimes: 2067`——看着还有很多。

真正的结构是:**有两个独立的额度**。一个是"剩余次数"(`remainUserTimes`),一个是"实验点余额"(`experiment_spot`)。前者是有次数但每次要扣点,后者是点数余额。`experiment_spot: 0` 时,有次数也扣不动。

而"每日免费名额"是第三个概念:`free_quota` 显示全局还剩 13 个,但 `spotsEnough: false` 说明你这会儿抢不到。**三个字段看着都在说"能不能做",实际是三个不同的闸门。**

## 四、状态码 200 也可能意味着失败

另一个反直觉的点:这个平台大量接口返回 **HTTP 200 但 body 里是 error_code**。

```json
HTTP/1.1 200 OK
{"error_code":"HD.31420023","error_msg":"lab instance not exist or has end"}
```

如果你只判断 `resp.status_code == 200` 就当成功,会拿到一堆"成功但什么也没发生"的结果。正确做法是两段判断:

```python
if r.status_code == 200 and "error_code" not in r.text:
ok()
else:
handle(r.text)
```

## 五、把"启动成功"和"资源就绪"分开看

还有一个容易混的:接口返回了一个 ID,不代表资源可用。

```
POST /exercise/start → 200 {"result": 504107} # 拿到会话 ID
POST /exercise/get-instance-info → {"instance_info": null, "status": "0"}
```

`status: "0"` 不是"正常",是"未分配"。我一开始把它当成功,等了五分钟才发现实例根本没起来。

**判断资源是否就绪,要看那个"有内容的字段"变没变**(`instance_info` 从 null 变对象),而不是看请求是否返回 200。

## 六、我现在用的排查顺序

1. **看状态码定性**:404 换路径,405 换方法,400 查参数,200 但带 error_code 查业务表。
2. **读措辞不看码**:400 的具体措辞比 400 本身信息量大得多。
3. **字段名做双写测试**:驼峰和下划线各试一次,成本极低。
4. **参数位置做互换测试**:body 和 query 互换。
5. **区分"接受"和"就绪"**:拿到 ID 只是受理,还要轮询到目标字段有值。
6. **早停**:同一类确定性失败(比如余额不足)试到第二次就该收手,不是重试能解决的。

## 最后

这套方法的核心其实一句话:**把报错当成服务端在告诉你它要什么,而不是在告诉你你做错了。**

`404` 是"这条路没有",`405` 是"门在这儿但别这么敲",`不能为空` 是"我看不懂你这个字段名",`无法转换` 是"你把它放错地方了"。把它当对话读,比翻文档快。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。