返回博客

FastAdmin 迁 ThinkPHP 8,成功码和错误码全部反转导致前端白屏

某后台管理系统从 FastAdmin/ThinkPHP 5 迁移到 ThinkPHP 8,迁移完成后部署上线,打开任意列表页,一片空白——没有报错弹窗,没有 loading 卡住,就是没有数据。打开 Network 面板,接口 200,Response 里数据完整,前端代码逻辑也没动过。

这种"API 正常、前端空白"的症状,排查半天很容易怀疑到缓存、跨域、权限,却很少第一时间往 response code 约定反转上想。

现象

  • 所有消费迁移后 Service 的页面,列表为空,但接口 HTTP 状态码 200
  • Network 面板里 code 字段是 1data 里有完整列表数据
  • 前端没有任何 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:

  1. 成功响应 code:1 → 前端判断 res.code === 0 为 false → 以为接口失败 → 不渲染数据
  2. 错误响应 code:0 → 前端判断 res.code === 0 为 true → 以为接口成功 → 尝试渲染 null → 空
坑:成功和失败的 code 同时反转,互相掩盖

两个方向都错,但错的方式不同:成功时前端以为失败(不渲染),失败时前端以为成功(渲染 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/
正解:迁移后统一走 ApiBase 包装,彻底消除手写 code 字段

最根本的修法是改 Controller,把 return json($this->service->...) 换成 return $this->success($data),让 ApiBase::success() 统一注入 code: 0。Service 层只返回业务数据,不再负责格式化 code 字段,从源头消除约定不一致的可能。

一句话外卖

从 FastAdmin/TP5 迁移 Service 到 ThinkPHP 8 时,第一件事是 grep "'code' =>" 全量扫描——两套框架的成功/失败码约定完全相反,漏改任何一处都会造成前端静默白屏或静默误报成功。