测吉凶错误处理与边界容错:ret_code 判断、空 expList、参数校验
# 测吉凶错误处理与边界容错:ret_code 判断、空 expList、参数校验
> 接口/接入点:测吉凶 apiCode=1617(四个接入点) · 免费服务 · 适用人群:已接入的中高级开发者 · 阅读时间:约 7 分钟
## 核心要点
- 成功判定要**双层**:外层 `showapi_res_code==0` 仅代表通道正常,业务必须再看 `showapi_res_body.ret_code=="0"`。
- 公司名接入点可能返回**空 expList**(`ret_code==0` 但无内容),须做空数组兜底,不能当失败也不能当异常。
- 免费档位有频限,正式上线建议加超时、基础参数校验与可选缓存,减少无效调用。
## Why:上线前必须想清楚的边界
开发联调时一切正常,一上线就出现「页面空白」「偶发报错」「额度秒空」——多半是边界没处理:把外层码当业务成功、公司名空结果当异常、没设超时与频控。本文把这些坑一次列清。
## What:三类需要处理的情形
| 情形 | 判断依据 | 正确处理 |
|------|------|------|
| 通道失败 | `showapi_res_code != 0` 或网络异常 | 提示系统错误,可重试 |
| 业务失败 | `showapi_res_body.ret_code != "0"` | 读 `remark` 给用户看原因 |
| 业务成功但空结果 | `ret_code=="0"` 且 `expList` 为空/缺失 | 显示「暂无分析」,非报错 |
## How:健壮调用模板(Python)
```python
import requests
def fortune(api_point, param_name, value, appkey):
url = f"https://route.showapi.com/1617-{api_point}"
try:
r = requests.post(url, params={"appKey": appkey},
data={param_name: value}, timeout=15)
r.raise_for_status()
except requests.RequestException as e:
return {"ok": False, "level": "channel", "msg": f"网络/通道异常:{e}"}
outer = r.json()
if outer.get("showapi_res_code") != 0:
return {"ok": False, "level": "channel", "msg": outer.get("showapi_res_error") or "通道错误"}
body = outer.get("showapi_res_body", {})
if body.get("ret_code") != "0":
return {"ok": False, "level": "biz", "msg": body.get("remark") or "业务失败"}
exp = body.get("expList") or []
if not exp:
return {"ok": True, "empty": True, "msg": body.get("remark") or "暂未返回分析"}
return {"ok": True, "empty": False, "expList": exp}
# 调用示例
print(fortune("1", "mobile", "13770000000", "YOUR_APPKEY"))
```
### 前端超时与参数校验(Node/浏览器)
```javascript
async function fortune(type, param, value, appkey) {
if (!value || !value.trim()) throw new Error("参数不能为空");
const url = `https://route.showapi.com/1617-${type}?appKey=${appkey}`;
const res = await fetch(url, { method: "POST",
body: new URLSearchParams({ [param]: value }), signal: AbortSignal.timeout(15000) });
const json = await res.json();
const b = json.showapi_res_body || {};
if (b.ret_code !== "0") throw new Error(b.remark || "业务失败");
return b.expList || []; // 空数组即「暂无分析」
}
```
### cURL(排查通道/业务错误)
```bash
curl -s -X POST "https://route.showapi.com/1617-4?appKey=YOUR_APPKEY" \
-H "content-type: application/x-www-form-urlencoded" \
--data-urlencode "companyName=某科技有限公司" | python -m json.tool
```
## 返回示例与解析
- 业务失败示例(如缺参数):`showapi_res_body.ret_code` 非 0,`remark` 给出原因 → 前端展示 `remark`。
- 公司名空结果示例:`ret_code:"0"`、`remark:"查询成功!"`、无 `expList` → 归为「成功但空」,展示友好空态。
## 进阶 / 边界
- **超时对齐文档**:接口 `x-read-timeout` 为 15s,示例统一设 `timeout=15` / `AbortSignal.timeout(15000)`。
- **缓存省额度**:`expList` 为「号码→结果」的确定性映射,可按 `type+input` 做缓存(如 Redis,TTL 数天),命中即返回,既提速又省免费档位。免费接口额度有限,缓存收益明显。
- **频控**:免费档位有调用限制,批量(如活动页多人同时测)建议加令牌桶限流,避免超限报错。
- **合规提示**:结果娱乐向,前端显著标注「仅供娱乐」,不要把「吉凶」当任何决策依据。
## FAQ
**Q:showapi_res_code 和 ret_code 都要判断吗?**
都要。`showapi_res_code` 是通道层(请求有没有正常被处理),`ret_code` 是业务层(有没有算出结果)。只看外层会漏掉业务失败。
**Q:公司名返回没 expList 算失败吗?**
不算。它是「ret_code=0 但空结果」,按成功分支展示「暂无分析」,不要报错也不要当异常重试。
**Q:免费接口要不要做缓存?**
建议做。`input→expList` 是确定性映射,缓存能省免费档位、提升响应。注意空结果也可缓存(避免重复空查)。
**Q:超时设多少合适?**
文档 read-timeout 为 15s,客户端超时设 15s 对齐即可;前端 AbortSignal.timeout(15000)。
## 相关能力 / 下一步阅读
- [测吉凶返回字段全解:ret_code、remark 与 expList 数组一文读懂](https://www.showapi.com/guides/fortune-response-fields-1617)
- [公司名测吉凶:接入点与「返回空 expList」边界说明](https://www.showapi.com/guides/fortune-company-guide-1617)
- [expList 数组结构化解析:把「数理/签语/吉凶/详解」拆成字段](https://www.showapi.com/guides/fortune-expList-parse-1617)
- **本系列共 13 篇**:查看[测吉凶指南总目录](https://www.showapi.com/guides/fortune-guides-1617)