应根据路由位置和返回类型选择:web.php 用 --resource(含 create/edit 视图方法),api.php 用 --api(跳过 create/edit,默认 JSON 响应);混用会导致 ViewNotFoundException 或 CSRF 419 错误。

Route::resource() 默认生成 7 个方法,但直接全量注册到生产环境几乎总是错的——它不区分 API 和 Web 场景,也不自动处理权限、验证或响应格式。
资源控制器该用 --resource 还是 --api?
看路由注册位置和返回内容类型:
- routes/web.php 中用 php artisan make:controller PostController --resource,生成含 create()、edit() 等返回 Blade 视图的方法
- routes/api.php 中必须用 php artisan make:controller PostController --api,它跳过 create 和 edit,且所有方法默认返回 JSON(不渲染视图)
- 混用会出问题:比如在 API 路由里注册 Web 风格的资源控制器,create() 方法被调用时抛出 ViewNotFoundException
- --api 不是“加了中间件”,只是删了两个方法 + 注释掉视图相关代码,别指望它自动加 auth:sanctum
Route::resource() 和 Route::apiResource() 的关键区别
二者生成的路由行为一致,但语义和默认中间件不同:
- Route::resource('posts', PostController::class) → 注册在 web 中间件组下,带 session、CSRF,适合表单提交
- Route::apiResource('posts', PostController::class) → 注册在 api 中间件组下,无 session、无 CSRF,适合 AJAX 或移动端调用
- 两者都支持 ->only() 和 ->except(),但 apiResource 默认就不含 create 和 edit 对应的 GET 路由
- 错误用法:在 api.php 里写 Route::resource(),会导致 POST 请求因缺少 CSRF token 被拦截(419)
为什么 update() 方法收不到 PATCH 请求?
浏览器原生表单只支持 GET/POST,所以 Laravel 要求你显式伪造方法:
- 前端必须在 POST 表单中加隐藏字段:<input name="_method" value="PATCH">
- 或用 JS 发起 fetch/PATCH 请求(无需伪造)
- 后端不会自动把 POST 当 PATCH 处理;Laravel 路由匹配严格依赖 HTTP 方法,_method 是中间件 Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull 之后才解析的
- 如果用 Postman 或 curl 测试,直接选 PATCH 方法即可,不用加 _method
- 检查是否误将路由注册在 web 组却没传 _token 字段,会导致 419 响应,掩盖真正的 405(Method Not Allowed)问题
验证逻辑放哪?别堆在 store() 和 update() 里
硬编码 $request->validate() 会导致三类问题:规则复用难、测试难、IDE 无法提示字段名
- 正确做法:运行 php artisan make:request StorePostRequest,在 rules() 中写验证数组
- 控制器方法签名改为 public function store(StorePostRequest $request),Laravel 自动拦截非法请求并返回 422
- update() 必须另建 UpdatePostRequest,不要靠 $this->route('post') 判断是否更新——Eloquent 模型绑定后,update() 方法收到的是已存在模型实例,但验证规则往往不同(比如 email 唯一性需排除自身)
- 表单请求类里可重写 authorize() 方法做权限检查,比在控制器里写 abort_if(!auth()->user()->can(...)) 更清晰
真正容易被忽略的是:资源控制器不是“开箱即用”的 CRUD 解决方案,而是一套命名与路由约定。一旦你开始改方法名、删参数、混用 Web/API 路由,就等于主动放弃这个约定带来的可维护性。与其后期修 bug,不如初始化时就选对 --api 或 --resource,再用 only() 显式声明需要的动作。


















