TP6的Route::resource()存在严重安全缺陷:only/except无效,强制注册全部7个REST方法;name命名硬编码、不可自定义;含参数路由须用显式HTTP方法注册并加pattern约束,否则易引发CSRF或注入风险。

ThinkPHP6 的 Route::resource() 生成的路由远比表面看起来“重”,它默认注册全部 7 个 RESTful 方法,哪怕你只写 ->only(['index','read']),也**无法阻止其他方法(如 create、edit、delete)的路由被注册和可访问**——这是最常被忽略的安全隐患。
Route::resource() 默认注册全部 7 个方法,only 不生效
在 TP6 中,Route::resource('article', 'Topic')->only(['index','read']) 看似只定义两个路由,但实际执行 php think route:list 会发现:article/create、article/store、article/destroy 等全部 7 条路由都存在,且状态为「enabled」。
-
only和except在 TP6 的resource()中是无效的(6.0.x 至今未修复) - 真正起作用的是手动逐条注册:
Route::get('article', 'Topic/index')+Route::get('article/:id', 'Topic/read') - 如果你依赖
url('[article.index]')这类命名路由生成 URL,resource()生成的 name 是article.index,但规则却是article,而你手写Route::get('article', ...)->name('article.index')才能完全可控
资源路由的 name 命名规则固定,与路径无关
Route::resource('user', 'User') 生成的路由 name 永远是 user.index、user.read、user.create 等,**不会因为你把路由前缀改成 member 就变成 member.index**。
- name 是硬编码拼接:控制器名小写 + 点 + 方法名,跟路由表达式完全脱钩
- 想自定义 name,必须放弃
resource(),改用显式命名:Route::get('member', 'User/index')->name('member.index') - 模板里用
url('[member.index]')才能稳定生成/member,而不是意外跳到/user
带参数的资源路由(如 read)必须用 Route::get() 显式注册
TP6 路由机制要求:含变量(如 :id)的路由,若需按 HTTP 方法严格限制(比如详情页只响应 GET),就不能用 Route::rule(),否则 POST 也能命中,带来 CSRF 风险。
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
立即学习“PHP免费学习笔记(深入)”;
- ✅ 正确:
Route::get('blog/:id', 'Topic/read')->name('blog.read') - ❌ 危险:
Route::rule('blog/:id', 'Topic/read')—— 默认接受所有 method,中间件和验证器可能错位 - 变量正则约束要加:
->pattern(['id' => '\d+']),避免传入非数字导致 SQL 注入或 404 - 如果参数含斜杠(如 Base64 token),用
\S+替代.*,防止贪婪匹配吞掉后续路由段
CLI 下查路由必须加 --with-route,否则看不到 resource 路由
php think route:list 默认不加载路由文件,输出为空或只有硬编码路径。不加参数时,你根本不知道 resource() 到底注册了什么。
- 查当前应用路由:运行
php think route:list index --with-route - 多应用模式下,确保路由文件在
app/index/route/app.php(不是根目录的route/) - 闭包路由(
function() { })不会出现在列表中,调试困难,线上禁用 - 如果
route:list仍无输出,先检查config/app.php中'app_route' => true是否开启
真正安全可控的资源式路由,不是靠 resource() 一行搞定,而是用 Route::get() / Route::post() 逐条声明 + 显式 ->name() + 严格 ->pattern()。TP6 的 resource() 更像一个历史兼容符号,而非现代开发推荐实践。


















