laravel路由必须按语义分层、生命周期约束组织:web.php用于会话认证(含session、csrf),适合表单提交和带跳转页面;api.php用于无状态token认证,适合vue/react调用的json接口;混用将导致session丢失或csrf漏洞。

直接说结论:Laravel 路由不是“写完就跑”,而是必须按语义分层、按生命周期约束来组织;闭包路由在生产环境无法缓存,Route::resource() 生成的路由默认不带中间件,api.php 和 web.php 的中间件栈完全不同——这些细节不厘清,轻则本地能跑线上 404,重则权限失控或 CSRF 漏洞。
怎么选 web.php 还是 api.php
别凭直觉。两者本质是两套独立中间件管道:web.php 自动套用 web 中间件组(含 session、csrf、encrypt_cookies),适合带登录态的页面跳转;api.php 默认走 api 组(无 session、无 csrf、依赖 token 认证),适合前后端分离或 App 接口。
- 表单提交、带跳转的登录页 → 必须用
web.php - Vue/React 前端调用的 JSON 接口 → 必须用
api.php - 混用会导致 session 丢失、CSRF token 不匹配,错误信息通常是
TokenMismatchException或空会话 -
api.php中的Route::get('/user', ...)不会自动读取 cookie,哪怕你写了auth:sanctum中间件,也得确保前端在请求头带上Authorization: Bearer xxx
为什么 Route::resource() 一注册就 404
常见错因不是路径写错,而是控制器方法名或命名空间没对齐。Laravel 的资源路由靠「约定」绑定动作,不是靠反射自动发现方法。
- 执行
php artisan make:controller PostController --resource后,必须保证该类里有index()、show($id)等七个标准方法,缺一个,对应路由就不可达 - 若控制器不在默认命名空间
App\Http\Controllers下(比如用了App\Http\Controllers\Api\v1\),Route::resource('posts', 'Api\v1\PostController')必须写全类名字符串,不能只写PostController::class(后者会解析成默认命名空间) - 资源路由不继承中间件:即使外层
Route::middleware(['auth'])->group(...)包裹了Route::resource(),中间件也不会自动挂到每个动作上,得显式链式调用->middleware('auth') - 调试时运行
php artisan route:list,确认输出中POST /posts对应的 Action 列是否为App\Http\Controllers\PostController@store,而不是Closure
路由分组里 middleware 链式调用和数组写法的区别
表面只是语法差异,实际影响中间件执行顺序和可维护性。
-
Route::middleware(['auth', 'throttle:60,1'])->group(...):中间件按数组顺序执行,auth先验身份,throttle再限流;若auth失败(如未登录),throttle根本不会触发 -
Route::group(['middleware' => 'auth'], ...):这是 Laravel 5.4 及更早写法,已过时;在 Laravel 12 中仍能工作,但无法链式追加其他配置(如prefix或as),容易导致重复嵌套 - 多个分组嵌套时,中间件栈是叠加的:外层
auth+ 内层can:delete-post,最终请求会经过两个中间件,且顺序固定不可逆 - 自定义中间件若需访问路由参数(如
{id}),必须在handle()方法里用$request->route('id')获取,不能依赖__invoke参数自动注入
路由缓存后为什么新增路由不生效
php artisan route:cache 是把所有路由编译成 PHP 数组并写入 bootstrap/cache/routes-v7.php,之后启动不再解析 routes/*.php 文件——这是性能优化,也是陷阱源头。
- 只要执行过缓存,后续改任何路由文件(包括增删
Route::get()、调整where()约束)都必须重新运行php artisan route:cache,否则无效 - 闭包路由(
function () { ... })无法被缓存,artisan 会直接报错退出;所以生产环境强制用控制器,禁用闭包 - 本地开发建议关掉缓存:
APP_ENV=local时route:cache默认不启用,但 CI/CD 流水线若漏掉这步,上线后新功能就静默失效 - 验证是否生效:查看
bootstrap/cache/routes-v7.php文件修改时间,或临时删掉它再访问任意路由,看是否报Target class does not exist类错误(说明正在走缓存)
最常被忽略的一点:路由匹配发生在中间件之前,但中间件里的逻辑(比如日志记录、权限校验)一旦抛异常,就不会走到控制器;而你在 routes/web.php 里写的 Route::fallback() 是最后兜底,但它本身也受外层分组中间件约束——如果 fallback 路由被包在一个需要登录的分组里,未登录用户连 404 页面都看不到,只会卡在登录跳转。











