thinkphp 6 的 jsonresponse 不会自动将业务错误码映射为 http 状态码,默认均为 200;需显式调用 code() 方法或使用 response()->json($data, $status) 设置状态码,并在异常处理器中按异常类型(如 validateexception→422、modelnotfoundexception→404)统一映射。

ThinkPHP 6 的 JsonResponse 不会自动映射业务错误码到 HTTP 状态码
默认情况下,json() 或 fail() 返回的都是 200 OK,哪怕你传了 400 或 500 作为业务码。HTTP 状态码和业务错误码是两套东西,TP6 默认只管后者。
常见错误现象:return json(['code' => 400, 'msg' => '参数错误'], 400) 看似传了状态码,但实际发出去仍是 200 —— 因为第二个参数被当成「JSON 编码选项」而非 HTTP 状态码(这是 PHP json_encode() 的坑)。
- 正确做法:必须显式调用
code()方法或使用response()->json()并传入第三个参数 -
return json(...)->code(400)是最直接的链式写法 - 若用
response()->json($data, $status = 200, $headers = [], $options = 0),第二个参数才是真正的 HTTP 状态码
全局统一拦截异常并映射 HTTP 状态码要用 app/exception/Handler.php
手动每个接口都 ->code(400) 不现实,尤其在验证失败、模型未找到、权限拒绝等场景下,得靠异常处理器自动识别类型并设状态码。
关键点在于:TP6 的 render() 方法里不能只返回 JSON 数据,还要改响应状态码。
- 遇到
ValidateException,设 HTTP 状态码为422(语义更准,比400更合适) - 遇到
ModelNotFoundException,设为404 - 遇到自定义的
BusinessException(继承HttpException),直接取其$statusCode属性 - 别忘了在
render()最后调用$response->code($code),否则状态码不会生效
validate() 失败时默认抛 ValidateException,但不带 HTTP 状态码
TP6 的验证器失败默认抛出 ValidateException,它本身没有绑定 HTTP 状态码,所以即使你在中间件或控制器里捕获它,$e->getStatusCode() 也不存在。
解决方式不是改验证器逻辑,而是让异常处理器“认出它并赋予语义”:
- 在
Handler.php的render()中判断$exception instanceof ValidateException - 构造一个标准响应:
response()->json(['code' => 422, 'msg' => $exception->getMessage(), 'data' => []], 422) - 注意:不要用
json()辅助函数,它不支持传入 HTTP 状态码参数 - 如果项目用了自定义验证基类,可在其中提前 throw 新异常(如
new HttpException(422, ...)),但没必要 —— 异常处理器处理更干净
双层码设计容易混淆:HTTP 状态码 ≠ 业务 code 字段
前端同学常以为 code: 401 就该对应 HTTP 401 Unauthorized,但实际两者职责不同:前者是业务流程标识(比如“登录过期”“余额不足”),后者是通信层语义(认证失败 / 资源不可达)。
典型陷阱:
- 把所有业务错误都塞进
500—— 这会让 Nginx / CDN / 前端请求库误判为服务崩溃 - 在
code字段里放200表示成功,又用 HTTP200,看似一致,但一旦要加重定向或缓存策略就全乱了 - API 网关或监控系统(如 Prometheus)只看 HTTP 状态码做告警,业务
code它根本读不到
真正需要花时间对齐的是:哪些业务错误该触发哪个 HTTP 状态码。比如“token 过期”建议用 401,“无权限访问某接口”用 403,“查不到订单”用 404,而不是全扔 200 再靠 code 区分。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











