必须在AppServiceProvider::boot()中用Response::macro('api', fn() => new ApiResponse())注册,宏需返回实现Responsable接口且支持链式调用的ApiResponse实例,每个setter方法末尾return $this,并确保控制器中显式return response()->api()->data(...)->message(...)。
怎么注册支持链式调用的 response()->api() 宏
直接在
里注册宏,而不是写在控制器或中间件里。宏必须返回一个对象(比如自定义类实例),才能支持
这种链式写法。
常见错误是返回
实例——它本身不支持链式调用;正确做法是返回一个实现了 fluent 接口的类,比如
,并在每个 setter 方法末尾
。
注册位置:只在
中调用
宏函数体必须返回一个支持链式调用的对象,不能直接返回
不要在宏里做条件判断(如是否 JSON 请求),那是中间件或控制器该干的事
若需兼容 Laravel 10 以下版本,注意
在 9.x 中已废弃,改用
为什么 response()->api()->data($user)->message('OK') 返回空响应
最常见原因是没在最后显式调用
或没让宏返回可被框架识别为
的对象。Laravel 只有在返回值实现了
接口,或本身就是
等原生响应类时,才会自动发送。
如果你的
类没实现
,或者宏里忘了
,而是写了
却没 return,那控制器方法就等于返回了
,最终响应为空。
检查
是否 implements
确认宏函数体内最后一行是
,不是
在控制器中必须写
,漏掉
就会静默失败
调试时可在宏里加
,确认返回的是你预期的类,不是
或其他意外类型
链式宏如何处理 HTTP 状态码与业务 code 的分离
HTTP 状态码(如 401、422)和业务 code(如
)必须分开管理,否则前端无法靠状态码做统一拦截,后端也容易混淆错误层级。
Laravel 13.2.0
PHP中文网提供Laravel 13.2.0版本下载,Laravel框架 是基于 PHP 8.3+ 的高性能框架,官方推荐通过 Composer 安装。它内置 AI SDK、JSON:API Resources 及原生向量搜索,支持属性驱动开发与队列路由,大幅提升开发效率。相比旧版,13.2.0 优化了缓存 TTL 管理与实时通信,无需 Redis 即可横向扩展。作为现代 Web 开发首选,它兼顾安全与极速体验,助您快速构建企业级应用。
下载
推荐方案:宏内部设两个独立属性 ——
控制响应头状态码,
放进 JSON body 作为业务标识。这样
才能同时满足协议规范和业务需求。
方法只影响
构造时的第二个参数,不修改 body 内的
避免把
和
混成同一个字段,否则 401 认证失败时 body 里还写
,前端逻辑全乱
验证失败(422)场景下,
必须是 422,但
可设为 1002,便于前端区分“参数错误”和“业务校验失败”
204 No Content 响应不能带 body,所以调用
时要主动清空
字段,否则 Laravel 会抛出异常
中间件里调用链式宏为什么失效
中间件拿到的是已生成的
实例,此时控制器早已执行完毕,
宏根本没机会运行。试图在中间件里再调一次宏,等于对一个已完成的响应做二次封装,极易导致双层 JSON 编码、header 丢失或状态码覆盖。
真正需要中间件介入的,只有原始响应非 JSON 格式(比如抛异常后返回 HTML 错误页)或需强制补 header 的极少数场景。绝大多数 API 响应应该由控制器主动构造,而非依赖中间件“打补丁”。
不要在中间件里写
,这是反模式
中间件适合做统一 header 注入(如
)、CORS 处理、日志记录等不改变 body 结构的操作
若坚持用中间件包装,只能针对
实例做内容重写,且必须保留原
和
,不能新建
认证失败(401)、授权失败(403)、模型未找到(404)这些响应,必须在
里统一构造,中间件完全插不上手
链式宏看着灵活,但真正落地时最容易栽在“谁负责触发”这个点上——它只该由控制器显式调用,不该被中间件、异常处理器或 Trait 自动触发。一旦跨出这个边界,就会出现响应为空、状态码错乱、body 被重复 encode 等难以追踪的问题。
AppServiceProvider::boot()->data()->message()->code()JsonResponseApiResponsereturn $thisAppServiceProvider::boot()Response::macro('api', ...)response()->json(...)Response::macro()ResponseFactory::macro()toResponse()ResponsableResponsableJsonResponseApiResponseResponsablereturn new ApiResponse()new ApiResponse()->data(...)nullApiResponseIlluminate\Contracts\Support\Responsablereturn new ApiResponse()new ApiResponse()->data(...)return response()->api()->data(...)returndd(get_class($this))stdClass"code": 4001$httpStatus$coderesponse()->api()->code(-401)->httpStatus(401)httpStatus()JsonResponsecodehttpStatus(200)code(4001)"code": 200httpStatuscodehttpStatus(204)dataResponseresponse()->api()response()->api()->data(...)X-Api-VersionJsonResponsegetStatusCode()headersresponse()->json()Handler::render()