应优先使用 response()->json(),适用于大多数API场景;仅当需字段过滤、关系预加载或统一序列化逻辑时才引入ApiResource类。

直接用 response()->json() 是最简单、最可靠的方式,不需要额外封装或引入资源类——除非你有字段过滤、关系预加载、序列化逻辑等复杂需求。
什么时候该用 response()->json()
适合大多数 API 场景:返回数组、对象、分页结果,或快速调试接口。它底层调用 PHP 的 json_encode(),默认设置 JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES,中文和 URL 安全。
- 返回简单数据:
return response()->json(['status' => 'success', 'data' => $user]);
- 自定义状态码和头信息:
return response()->json(['error' => 'Not found'], 404)->header('X-Api-Version', 'v1'); - 配合
withHeaders()添加跨域头(开发期临时用):return response()->json($data)->withHeaders(['Access-Control-Allow-Origin' => '*']);
什么时候该用 ApiResource 类
当你需要统一控制模型输出结构、隐藏敏感字段、动态包含关联数据、或复用序列化逻辑时,ApiResource 才值得引入。它不是“更高级”,而是“更重”。
- 字段脱敏:在
UserResource中只暴露id、name、avatar,不返回password_hash或remember_token - 嵌套关系处理:用
->whenLoaded('posts')避免 N+1,且只在显式请求?include=posts时加载 - 条件字段:比如
->when(auth()->id() === $this->user_id, 'is_owner') - 注意:资源类默认不自动处理分页元信息,需搭配
ResourceCollection或使用JsonResource::collection()
toArray() 里别写数据库查询
资源类的 toArray() 方法是同步执行的,如果在里面调用 $this->posts->count() 或 $this->profile->bio(未预加载),会触发懒加载,导致严重性能问题。
- 正确做法:控制器中预加载所需关系:
User::with(['posts', 'profile'])->findOrFail($id);
- 避免在
toArray()里调用$this->load(...)或DB::table(...)->first() - 如需统计类字段(如文章数),优先用模型的
withCount():User::withCount('posts')->get(),然后在资源中直接取$this->posts_count
响应格式不一致?检查中间件和异常处理器
即使代码里用了 response()->json(),也可能被中间件覆盖 Content-Type,或被 App\Exceptions\Handler 拦截后转成 HTML 错误页。
- 确认没有中间件(如某些 JWT 验证中间件)意外调用
abort(401)但未配置为 JSON 响应 - 检查
App\Exceptions\Handler::render()是否对 API 路由做了特殊处理;Laravel 9+ 默认会识别Accept: application/json并返回 JSON 错误,但自定义逻辑可能绕过它 - 测试时用
curl -H "Accept: application/json"显式声明,避免浏览器直接访问触发 HTML fallback
真正容易被忽略的是:API 路由没加 api 中间件组,导致 CSRF 验证失败、Session 启动、甚至重定向到登录页——这时候你看到的“不是 JSON”根本不是响应格式问题,而是整个流程被拦下了。









