应重写 think\response\Json 类并绑定容器,再用中间件兜底非 Json 响应,同时单独处理 CLI 场景;核心是控制类实例化路径、拦截时机和补全盲区。

ThinkPHP 6 默认 Ajax 响应结构不统一?直接改 app\common\behavior\ResponseBehavior 不现实
ThinkPHP 6 的 Ajax 响应默认走 think\Response,但控制器里写 return json([...]) 和抛异常触发的 think\exception\HttpException 响应结构不一致——前者是纯数据,后者带 code/msg/data 包裹。这不是配置能一键对齐的,得从响应生成链路切入。
真正可控的入口是全局响应封装层:app\common\behavior\ResponseBehavior 确实存在,但它只处理「已生成的 Response 对象」,无法干预 json()、success() 这类快捷方法的原始输出结构。别在这儿绕弯子。
- 所有
return json(...)实际调用的是think\Response::create(),底层用think\response\Json - 想统一结构,必须重写
think\response\Json类,或在中间件中拦截并重写响应体 - 重写类更干净,但要注意 Composer 自动加载路径和命名空间是否被框架覆盖
替换 think\response\Json 类:继承 + 重写 output 方法
ThinkPHP 允许通过容器绑定替换内置响应类。关键不是“怎么配”,而是“在哪替、替什么”。直接在 app\common\provider\AppServiceProvider 的 register 方法里绑定新类即可:
public function register()
{
$this->app->bind('think\response\Json', \app\common\response\Json::class);
}
然后新建 app\common\response\Json.php,继承原类,只重写 output:
namespace app\common\response;
use think\response\Json as BaseJson;
class Json extends BaseJson
{
protected function output($data): string
{
// 统一包装成 { "code": 0, "msg": "ok", "data": [...] }
$result = [
'code' => $this->code ?: 0,
'msg' => $this->message ?: 'ok',
'data' => $data,
];
return json_encode($result, JSON_UNESCAPED_UNICODE);
}
}
- 别动
__construct或init,否则可能破坏code/message的自动推导逻辑 -
$this->code来自return json($data, 200, [], ['code' => 1])中的第四个参数,兼容原有写法 - 若需支持
return json(['status' => 'ok'])这种旧格式,得额外判断$data是否已是数组且含code键——但不建议,会增加歧义
中间件方式补救:对非 Json 响应(如异常)做兜底包装
上面改了 json(),但 throw new ValidateException('xxx') 或未捕获异常仍返回原生结构。这时不能靠改异常类,而应在响应发送前统一拦截——用中间件最稳妥。
新建 app\common\middleware\ApiResponseMiddleware.php,在 handle 末尾检查响应类型:
if ($response instanceof \think\Response && ! $response instanceof \think\response\Json) {
$originalData = $response->getData();
$newData = [
'code' => $response->getCode() ?: 500,
'msg' => $response->getHeader('X-Error-Message') ?: 'server error',
'data' => is_array($originalData) ? $originalData : [],
];
$response = \think\Response::create($newData, 'json', $response->getCode());
}
- 必须放在全局中间件队列末尾(
app/middleware.php中靠后注册),否则会被其他中间件覆盖 - 不要试图修改
Html或View响应,API 场景下它们不该出现;只针对非Json的Response实例做转换 -
getHeader('X-Error-Message')是个技巧:你在异常处理器里手动 setHeader,比解析异常消息字符串更可靠
别漏掉命令行和单元测试场景:CLI 下 json() 仍走原逻辑
TP6 的命令行执行(php think xxx)和 PHPUnit 测试时,App::isCli() 为 true,think\response\Json 的 output 方法压根不会被调用——它直接返回原始数组,不 JSON 编码。这意味着你刚写的 output 重写在 CLI 下完全失效。
- 命令行接口若需输出 JSON,显式调用
echo json_encode(...),别依赖return json(...) - 单元测试中验证 API 响应,用
$this->get('/api/xxx')->assertJson([...])没问题,因为测试走 HTTP 内核,不是 CLI - 如果真要 CLI 下也走统一格式,得单独写一个
cli_json()辅助函数,框架层面不提供这种钩子
统一响应这事,核心不在“配”,而在“控”——控住 Json 类的实例化路径,控住中间件的拦截时机,再补上 CLI 这个盲区。改一处,漏三处,是常态。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!










