在ThinkPHP 6.0.16中,应严格区分HTTP状态码(如200、400、401)与业务码(如1001、2005),前者由客户端识别影响协议行为,后者供前端逻辑判断;控制器中用json()函数显式传入HTTP状态码,异常处理需自定义BusinessException并统一在Handler::render中返回结构化JSON。

在 ThinkPHP 6.0.16 中为接口开发设置自定义响应状态码,核心在于区分「HTTP 状态码」和「业务状态码」,二者作用不同、使用场景不同,不能混用或互相替代。
明确 HTTP 状态码与业务码的分工
HTTP 状态码(如 200、400、401、403、404、500)由浏览器或客户端识别,影响重定向、缓存、请求重试等行为;业务码(如 1001、2002、3005)是纯业务逻辑标识,只供前端 JS 判断分支流程,不参与 HTTP 协议交互。
- 登录失败应返回 401(未认证)或 403(禁止访问),同时携带业务码
{"code": 1001, "msg": "token 已过期"} - 参数校验不通过应返回 400,而非 200 + code=400 —— 否则前端 fetch 的
response.ok仍为 true,容易掩盖错误 - 数据库唯一约束冲突(如手机号重复),不应直接暴露 MySQL 错误码 1062,而应映射为统一业务码
2005,并配以 HTTP 状态码 400
在控制器中正确返回带状态码的 JSON 响应
TP6 提供了 json() 助手函数,可直接传入 HTTP 状态码,无需手动构造 Response 对象。
- 成功响应:
return json(['code' => 0, 'msg' => 'ok', 'data' => $user], 200); - 客户端错误:
return json(['code' => 1002, 'msg' => '缺少必要参数'], 400); - 权限不足:
return json(['code' => 1003, 'msg' => '无操作权限'], 403); - 资源不存在:
return json(['code' => 1004, 'msg' => '用户不存在'], 404);
注意:不要只写 return ['code'=>0, 'data'=>$data] —— 这会默认走 200 状态码且 Content-Type 可能不是 application/json,尤其在中间件或异常拦截后易出错。
立即学习“PHP免费学习笔记(深入)”;
统一异常处理中注入状态码与业务码
所有非预期错误(包括主动抛出的业务异常)都应经过 app/exception/Handler.php 的 render 方法拦截。这里必须判断异常类型,构造结构化 JSON 并指定 HTTP 状态码。
- 新建
app/exception/BusinessException.php,继承hinkException,构造函数接收$code、$message、$httpCode = 400 - 在
Handler::render()中判断$e instanceof BusinessException,然后返回:json(['code' => $e->getCode(), 'msg' => $e->getMessage()], $e->getHttpCode()) - 确保该 Handler 类实现了
thinkcontractExceptionHandlerInterface,否则配置无效 - 抛出方式示例:
throw new BusinessException(2005, '手机号已被注册', 400);
避免常见配置陷阱
不少开发者改了代码却没生效,往往卡在配置或缓存环节:
- 确认
config/exception.php中'handle'配置项拼写正确,值为完整命名空间字符串,如'\app\exception\Handler' - 修改 Handler 后务必执行
php think clear:config清除配置缓存 - 若使用多应用模式(如 api/、admin/),每个子应用的
exception.php都需单独配置对应 Handler - 临时加一行
file_put_contents(RUNTIME_PATH . '/handler.log', date('Y-m-d H:i:s') . " ", FILE_APPEND);到 render 开头,验证是否真正触发



















