
控制器返回视图还是 JSON,取决于你响应的用途和客户端期望的数据格式——不是靠写法猜,而是靠 HTTP Accept 头、路由语义或显式约定来决定。
判断依据:看请求头 Accept 字段是否含 application/json
浏览器直接访问 /user/1,Accept 通常是 text/html;前端用 fetch 或 Axios 调 API 时,默认发 Accept: application/json。Laravel 不自动识别这个差异,但你可以手动检查:
- 用
request()->expectsJson()判断是否为 AJAX/JSON 请求(它本质是检查Accept头是否匹配application/json或包含+json) - 不要只靠
request()->ajax(),它只检测X-Requested-With: XMLHttpRequest,而现代 Fetch 默认不带这个头 - 如果项目明确区分前后端(比如 Vue SPA + Laravel API),建议直接按路由分层:
/api/*强制返回 JSON,/或/admin/*返回视图
返回视图:用 view() 或 response()->view()
返回 HTML 页面时,必须传入存在且可渲染的 Blade 模板路径。常见错误是模板名拼错或未编译:
- 模板文件必须在
resources/views/下,如resources/views/user/show.blade.php,调用时写view('user.show'),不能写view('user/show')(点号代替斜杠) - 若传入空数据或 null 给视图,Blade 不报错但可能渲染异常;建议用
compact()或数组明确传参,例如view('user.show', compact('user')) - 不要在返回视图前调用
response()->json()或其他链式方法,view()返回的是Illuminate\Http\Response实例,但已封装好 Content-Type 为text/html,再链header()可能覆盖关键头
返回 JSON:用 response()->json() 或直接返回数组
两种写法等价,但行为细节不同:
- 直接
return ['data' => $user];会触发 Laravel 的自动 JSON 转换,状态码默认 200,Content-Type 自动设为application/json -
return response()->json(['data' => $user], 201);更可控:可指定状态码(如创建资源用 201)、自定义 JSON 选项(如JSON_UNESCAPED_UNICODE)、甚至加响应头:->withHeaders(['X-Api-Version' => 'v2']) - 注意:Eloquent 模型或集合直接返回也会自动 JSON 化,但会触发
toArray()和隐藏字段逻辑($hidden、$casts等生效);若需原始字段,先调$model->getAttributes() - 别用
json_encode()手动编码再 return,这样丢失了 Laravel 的响应对象能力(无法链式加 header、cookie),且 Content-Type 不会自动设置
混用场景:同一个控制器动作返回不同格式
比如用户资料页既要支持浏览器直访(返回视图),又要支持前端异步拉取(返回 JSON)。这时推荐显式分支,而不是依赖模糊判断:
- 最稳妥:用路由前缀区分,
Route::get('/user/{id}', [UserController::class, 'show']);返回视图;Route::get('/api/user/{id}', [UserController::class, 'apiShow']);返回 JSON - 次选:在动作内用
if (request()->expectsJson())分支,但要确保所有分支都 return 响应,避免漏写导致 500 - 避免:在视图里用
@if(request()->expectsJson())输出 JSON —— 这违反响应职责分离,且视图不该决定响应类型
真正容易被忽略的点是:返回 JSON 时,如果模型里有循环引用(比如 User → Posts → User),response()->json() 会抛出 Illuminate\Contracts\Database\ModelNotFoundException 或静默截断;必须提前用 withoutRelations() 或 makeHidden() 处理,而不是等报错才查。











