FastAdmin 迁 ThinkPHP 8,成功码和错误码全部反转导致前端白屏
某后台管理系统从 FastAdmin/ThinkPHP 5 迁移到 ThinkPHP 8,迁移完成后部署上线,打开任意列表页,一片空白——没有报错弹窗,没有 loading 卡住,就是没有数据。打开 Network 面板,接口 200,Response 里数据完整,前端代码逻辑也没动过。
这种"API 正常、前端空白"的症状,排查半天很容易怀疑到缓存、跨域、权限,却很少第一时间往 response code 约定反转上想。
现象
- 所有消费迁移后 Service 的页面,列表为空,但接口 HTTP 状态码 200
- Network 面板里
code字段是1,data里有完整列表数据 - 前端没有任何 console 错误,只是渲染了空数组
- 提交表单时,即使参数完全正确,也会收到一个
code: 0的"成功"响应,却没有任何业务操作发生
根因
FastAdmin(基于 ThinkPHP 5)与 ThinkPHP 8 原生框架对 code 字段的约定完全相反:
| 框架 | 成功 | 失败 |
|------|------|------|
| FastAdmin / TP5 | code: 1 | code: 0 |
| ThinkPHP 8 原生 | code: 0 | code: 42200 / 50000 |
迁移时,Service 类的代码被直接复制过来,return ['code' => 1, 'data' => [...]] 这类写法原封不动。更致命的是,Controller 层没有走 ApiBase::success() 包装,而是直接 return json($this->service->method()),绕过了框架层统一格式化的机会:
// Controller 绕过了 ApiBase,直接透传 Service 的原始返回
return json($this->service->getPageList($this->request->post()));这导致双向静默 bug:
- 成功响应
code:1→ 前端判断res.code === 0为 false → 以为接口失败 → 不渲染数据 - 错误响应
code:0→ 前端判断res.code === 0为 true → 以为接口成功 → 尝试渲染 null → 空
两个方向都错,但错的方式不同:成功时前端以为失败(不渲染),失败时前端以为成功(渲染 null)。在测试阶段很容易只发现"数据不显示",而错误路径的 bug 要到真正触发错误时才暴露——此时前端表现是"提交成功了但什么都没发生",极难定位。
正解
第一步:定位受影响文件
# 找出所有还在用旧约定的 Service
grep -rn "'code' => 1" app/common/service/
# 找出绕过 ApiBase 的 Controller
grep -rn "return json(\$this->service->" app/admin/controller/ app/mp/controller/第二步:修正成功响应
// 修改前(FastAdmin 约定)
return ['code' => 1, 'data' => ['list' => $list, 'total' => $total], 'msg' => 'success'];
// 修改后(TP8 约定)
return ['code' => 0, 'data' => ['list' => $list, 'total' => $total], 'msg' => 'success'];第三步:修正错误响应(最容易漏!)
// 修改前(FastAdmin:code:0 = 错误)
return ['code' => 0, 'msg' => '参数不能为空', 'data' => null];
// 修改后(TP8:42200 = 参数校验失败,50000 = 服务器错误)
return ['code' => 42200, 'msg' => '参数不能为空', 'data' => null];第四步:区分成功行和错误行的技巧
成功行通常包含:'success'、'修改成功'、'新增成功'、'删除成功' 等字样。
错误行通常包含:'不能为空'、'缺少'、'不存在'、'失败' 等字样。
注意:多行 return 语句可能把 'code' 和 'msg' 拆在不同行,脚本批量替换时需要结合上下文判断,不能只看当前行。
第五步:修完扫尾
# 确保没有残留
grep -rn "'code' => 1" app/common/service/ app/admin/controller/ app/mp/controller/最根本的修法是改 Controller,把 return json($this->service->...) 换成 return $this->success($data),让 ApiBase::success() 统一注入 code: 0。Service 层只返回业务数据,不再负责格式化 code 字段,从源头消除约定不一致的可能。
一句话外卖
从 FastAdmin/TP5 迁移 Service 到 ThinkPHP 8 时,第一件事是
grep "'code' =>"全量扫描——两套框架的成功/失败码约定完全相反,漏改任何一处都会造成前端静默白屏或静默误报成功。