关于 routing-patterns
routing-patterns 是供 AI Agent 审查、生成和更新 Rails 路由的技能,涵盖可复用 concern、浅层嵌套及路由配置模式,帮助维护应用路由。
主要功能
- 01可复用路由 concern
- 02浅层嵌套路由
- 03明确限制资源操作
- 04嵌套与单数资源
- 05自定义参数和解析器
- 06基于约束的路由
适用场景
- 01审查 Rails 路由文件
- 02生成可复用的资源路由
- 03缩短深层嵌套 URL
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ rolemodel/ rolemodel-skills --skill routing-patterns安装后在 Claude Code 中输入/routing-patterns确认已加载。
详细介绍
路由最佳实践
目的
此技能可帮助 AI Agent 按照专业模式和最佳实践审查、生成和更新 Rails 路由。内容涵盖 RESTful 资源路由、用于代码复用的路由关注项、浅层嵌套策略,以及可应用于任何 Rails 应用的高级路由配置。
背景
此技能涵盖:
- 遵循 RESTful 约定的 Rails 路由
- 遵循 DRY(Don't Repeat Yourself,不要重复自己)原则的 路由关注项
- 避免 URL 过长的 浅层嵌套
- 灵活路由所需的 自定义参数和元数据
- 用于多态路径的 自定义路由解析器
- 用于授权的 基于约束的路由
- 用于维护路由文件的 组织模式
最佳实践
1. 使用路由关注项复用行为
在 routes.rb 顶部定义关注项,用于多个资源共享的行为。这能让路由保持 DRY 且易于维护。
示例:可评论资源
concern :commentable do
resources :comments, commentable_type: parent_resource.name.classify
end
示例:可复制资源
concern :duplicatable do
resources :duplications, only: %i[create], resource_type: parent_resource.name.classify
end
动态表单使用 :turbo_form 关注项,该关注项由 turbo_form gem 的引擎定义。请直接应用,不要自行定义。参见 dynamic-forms 技能。
要点:
- 关注项将通用的嵌套资源模式提取为可复用模块
- 使用
parent_resource.name.classify,动态地将父级上下文类型传递给控制器 - 适当时使用
only:或except:限制操作 - 自定义参数(例如
commentable_type、resource_type)会作为元数据传递给控制器 - 控制器通过
rolemodel_railsgem(>= 2.4.0)提供的resource_for(:commentable_type),将这些元数据还原为父级记录
编写控制器前,请先阅读 polymorphic-parent-resources 技能。 传递 *_type 默认值只是整个模式的一部分——该技能介绍了控制器端的处理方式、浅层嵌套下必需的 before_action 作用域(成员路由会继承错误的 *_type),以及如何整合现有的按父级划分的控制器。
2. 将关注项应用于资源
通过 concerns: 选项和符号数组应用关注项:
resources :products, concerns: %i[duplicatable turbo_form]
resources :articles, concerns: %i[commentable duplicatable turbo_form]
优点:
- 消除重复的嵌套资源定义
- 共享行为只需在一处更新
- 能清楚地看出哪些资源共享通用功能
3. 浅层嵌套策略
使用 scope shallow: true 包装器为所有嵌套资源启用浅层嵌套,避免 URL 因嵌套层级过深而变得冗长。
scope shallow: true do
resources :projects do
resources :tasks do
resources :comments
# comments routes are shallow - only :index and :create are nested
# show/edit/update/destroy use /comments/:id instead of /projects/:project_id/tasks/:task_id/comments/:id
end
end
end
end
优点:
- 成员操作(show、edit、update、destroy)的 URL 更短、更简洁
- 只有集合操作(index、create)仍嵌套在父级之下
- 单个资源的 URL 更易于收藏和分享
- 需要完整嵌套时,可通过
shallow: false覆盖
生成的路由:
# Nested (collection routes)
GET /projects/:project_id/tasks tasks#index
POST /projects/:project_id/tasks tasks#create
GET /projects/:project_id/tasks/new tasks#new
# Shallow (member routes)
GET /tasks/:id tasks#show
GET /tasks/:id/edit tasks#edit
PATCH /tasks/:id tasks#update
DELETE /tasks/:id tasks#destroy
覆盖示例:
concern :assembly do
# Keep full nesting when parent context is always needed
resources :assembly_items, only: %i[show], param: :kind, shallow: false
end
注意事项——浅层成员路由和按父级设置的默认值。 将包含成员操作的关注项应用于多个父级时,Rails 会为每个父级绘制浅层成员路径(/tasks/:id)。只有第一个路由会保留路由名称,其余都是无法访问的重复路由。因此,该关注项上的任何自定义默认值(例如 commentable_type),在每个成员请求中都会解析为第一个绘制的父级,无论记录实际属于哪个父级。控制器不得在成员操作中读取 *_type 参数;请将该 before_action 限定在集合操作中。参见 polymorphic-parent-resources。
4. 使用 only: 和 except: 限制操作
始终明确资源提供哪些操作。这有助于提升安全性、性能和代码清晰度。
# Only specific actions
resources :duplications, only: %i[create] # Only create action needed
resources :previews, only: %i[show update] # Only show and update
# All except specific actions
resources :automations, except: %i[show] # All standard actions except show
resources :notes, except: %i[show] # Create/edit/destroy but no individual view
resources :widgets, except: %i[index show] # No collection or individual views
原因:
- 避免未使用的路由使
rails routes输出变得杂乱 - 阻止访问尚未实现的控制器操作
- 清楚地向其他开发者说明意图
- 不公开不必要的端点,缩小攻击面
5. 嵌套资源
对于属于某个父资源的资源,请进行适当嵌套:
resources :companies do
resources :employees, except: %i[index show]
end
resources :products do
resources :reviews, except: %i[index]
resources :variants, except: %i[index show]
end
常见模式:
- 父资源默认拥有完整的 CRUD 操作
- 嵌套资源通常不包含
index(在父资源的 show 页面中显示) - 嵌套资源通常不包含
show(在父资源视图中内联编辑或编辑) - 嵌套资源的创建、更新和销毁操作在父资源上下文中执行
6. 单数资源
对于每个父资源只有一个的资源,请使用单数形式的 resource:
resources :users do
resource :profile, only: %i[show edit update] # Only one profile per user
resource :settings, only: %i[edit update] # Only one settings per user
resource :avatar, only: %i[show update] # Only one avatar per user
end
要点:
- 单数资源没有
index操作 - URL 不需要
:id参数(例如,使用/users/1/profile,而不是/users/1/profiles/1) - 非常适合一对一关系或单例资源
7. 集合路由和成员路由
使用 on: :collection 或 on: :member 添加自定义路由:
resources :products do
# Collection routes (no :id needed)
get :search, on: :collection # /products/search
post :bulk_import, on: :collection # /products/bulk_import
# Member routes (requires :id)
post :duplicate, on: :member # /products/:id/duplicate
patch :archive, on: :member # /products/:id/archive
get :preview, on: :member # /products/:id/preview
end
# In a concern
concern :archivable do
patch :archive, on: :member
patch :unarchive, on: :member
end
要点:
- 集合路由作用于整个集合(没有
:id参数) - 成员路由作用于单个资源(需要
:id参数) - 使用合适的 HTTP 动词(读取用 GET,创建用 POST,更新用 PATCH/PUT,删除用 DELETE)
8. 自定义参数
使用 param: 覆盖默认参数名称:
resources :products, param: :slug # Uses :slug instead of :id
# In concerns
concern :categorizable do
resources :categories, only: %i[show], param: :slug
end
结果:
- URL 会变为
/products/:slug,而不是/products/:id - 控制器接收的是
params[:slug],而不是params[:id] - 示例:
/products/vintage-leather-jacket,而不是/products/123 - 适用于 SEO 友好的 URL,或使用非数字标识符的情况
9. 默认选项
为资源设置默认选项:
resources :categories, defaults: { subcategory: false }
这些默认值可通过 params[:subcategory] 获取。
10. 自定义路由解析器
为多态路径辅助方法定义自定义解析器:
resolve 'Bulk::Accessories' do |form|
form.persisted? ? [form.accessory] : [form.tank, :accessories]
end
resolve 'AssemblyItem' do |item|
[item.host, item]
end
**用法:**这样可以让 url_for(@form_object) 或 link_to(@assembly_item) 正常工作。
11. 会话路由
对相关的身份验证操作,使用带有 scope 的 controller 块:
controller :sessions do
get :login, action: :new
delete :logout, action: :destroy
scope :auth do
get :failure
match 'ADFS/callback', action: :create, via: %i[get post], as: :adfs_callback
end
end
12. 使用约束挂载引擎
使用身份验证约束挂载管理引擎:
# Allow access only if user is logged in and is admin
mount PgHero::Engine, at: :pghero,
constraints: -> env {
env.session[:user_id].present? &&
User.find_by(id: env.session[:user_id])&.admin?
}
# Redirect to login if not authenticated
get :pghero, to: redirect('/login'), anchor: false,
constraints: -> env { env.session[:user_id].blank? }
13. 健康检查路由
# Production health check
mount Health::Check::Engine, at: 'health-check' if Rails.env.production?
# Rails 7.1+ health check
get :up, to: 'rails/health#show', as: :rails_health_check
完整示例结构
以下是一个遵循所有最佳实践、组织良好的路由文件:
Rails.application.routes.draw do
# 1. Define concerns first (reusable route patterns)
concern :commentable do
resources :comments, commentable_type: parent_resource.name.classify
end
concern :archivable do
patch :archive, on: :member
patch :unarchive, on: :member
end
concern :searchable do
get :search, on: :collection
end
# 2. Main routes with shallow nesting
scope shallow: true do
# Top-level resources
resources :users, except: %i[show] do
resource :profile, only: %i[show edit update]
resource :settings, only: %i[edit update]
end
resources :products, concerns: %i[commentable archivable searchable] do
resources :reviews, except: %i[index]
resources :variants, except: %i[index show]
end
# Nested resources
resources :projects do
resources :tasks, concerns: %i[commentable] do
resource :assignment, only: %i[create destroy]
end
end
end
# 3. Session/authentication routes
controller :sessions do
get :login, action: :new
post :login, action: :create
delete :logout, action: :destroy
end
# 4. Admin routes with constraints
namespace :admin do
resources :users
resources :settings, only: %i[index update]
end
# 5. Mounted engines (with constraints if needed)
mount Sidekiq::Web, at: '/sidekiq', constraints: AdminConstraint.new
# 6. Custom route resolvers (for polymorphic routing)
resolve 'ProjectTask' do |task|
[task.project, task]
end
# 7. Health checks
get :up, to: 'rails/health#show', as: :rails_health_check
# 8. Root route
root 'dashboard#index'
end
审查清单
审查或生成路由时,请确认:
- Concern 定义在文件顶部
- Concern 遵循 DRY 原则,可在多个资源间复用
- 自定义参数(如
commentable_type)使用parent_resource.name.classify - 传递
*_type默认值的 Concern 有对应的控制器,且该控制器使用resource_for(参见polymorphic-parent-resources) - 使用
scope shallow: true启用浅层嵌套 - 资源明确使用
only:或except:限制操作 - 嵌套资源遵循该模式(通常不包含 index/show)
- 单数资源使用
resource,而不是resources - 集合路由/成员路由使用
on:参数 - 需要时使用
param:指定自定义参数名称 - 为表单对象和多态模型定义路由解析器
- 管理引擎配置了身份验证约束
- 定义了根路由
- 路由按逻辑顺序组织(concern → 资源 → 自定义路由 → 引擎 → 解析器 → 根路由)
应避免的反模式
❌ 不要重复嵌套资源模式:
resources :articles do
resources :comments, commentable_type: 'Article'
end
resources :posts do
resources :comments, commentable_type: 'Post'
end
✅ 改用 Concern:
concern :commentable do
resources :comments, commentable_type: parent_resource.name.classify
end
resources :articles, concerns: %i[commentable]
resources :posts, concerns: %i[commentable]
❌ 不要在未启用 shallow 的情况下进行深层嵌套:
resources :companies do
resources :projects do
resources :tasks do
# Results in /companies/:company_id/projects/:project_id/tasks/:id/edit
end
end
end
✅ 使用浅层嵌套:
scope shallow: true do
resources :companies do
resources :projects do
resources :tasks # edit becomes /tasks/:id/edit
end
end
end
❌ 不需要时不要保留所有操作:
resources :duplications # Provides 7 REST actions but only need create
✅ 明确指定:
resources :duplications, only: %i[create]
❌ 单数资源不要使用复数形式:
resources :profiles, only: %i[show] # There's only one profile per user
✅ 使用单数资源:
resource :profile, only: %i[show]
❌ 不要对上下文类型进行硬编码:
concern :commentable do
resources :comments, commentable_type: 'Article' # Only works for articles
end
✅ 使用 parent_resource:
concern :commentable do
resources :comments, commentable_type: parent_resource.name.classify
end
高级模式
带有嵌套 Concern 的 Concern
Concern 可以引用其他 Concern,以构建高度可复用的路由模式:
concern :searchable do
get :search, on: :collection
get :autocomplete, on: :collection
end
concern :taggable do
resources :tags, only: %i[index create destroy],
concerns: %i[searchable],
taggable_type: parent_resource.name.classify
end
resources :articles, concerns: %i[taggable]
# Articles get tags with search and autocomplete functionality
多个自定义参数
可以向 Concern 传递多个自定义参数:
concern :versioned do
resources :versions,
only: %i[index show],
param: :version_number,
shallow: false,
versionable_type: parent_resource.name.classify
end
这些自定义参数可在控制器中作为路由元数据使用:
# In controller
def index
@versionable_type = request.path_parameters[:versionable_type] # => "Article"
end
特定上下文的路由扩展
应用 concern 后,为特定资源添加其他路由:
resources :articles, concerns: %i[commentable] do
# Add article-specific routes beyond the concern
get :preview, on: :member
post :publish, on: :member
end
按条件挂载引擎
根据环境按条件挂载引擎:
if Rails.env.production?
mount HealthCheck::Engine, at: 'health-check'
end
unless Rails.env.production?
mount LetterOpenerWeb::Engine, at: '/letter_opener'
end
常见路由模式
这些模式在 Rails 应用中很常见,可通过 concern 实现:
可评论的资源:
- 模式:可以添加评论的资源
- 实现:使用多态关联的嵌套评论资源
- 应用于:文章、博客帖子、产品、任务等
可复制的资源:
- 模式:可以克隆/复制的资源
- 实现:为复制操作创建仅支持创建的嵌套资源
- 应用于:模板、文档、配置等
可归档的资源:
- 模式:可以归档/取消归档的资源
- 实现:用于更改归档状态的成员路由
- 应用于:项目、文档、记录等
可搜索的资源:
- 模式:具有搜索/筛选功能的资源
- 实现:用于搜索操作的集合路由
- 应用于:产品、用户、文章等
有版本历史的资源:
- 模式:具有版本历史的资源
- 实现:嵌套版本资源,通常为只读
- 应用于:文档、API 资源、配置等
可标记的资源:
- 模式:可以添加标签/分类的资源
- 实现:带有创建/销毁操作的嵌套标签
- 应用于:文章、图片、书签等
AI Agent 使用说明
当被要求审查路由时:
- 检查路由是否按以下顺序组织:concern → 资源 → 自定义路由 → 引擎 → 解析器 → 根路由
- 验证 concern 定义正确且可复用
- 检查是否存在应使用浅层嵌套的深层嵌套
- 确保资源适当地使用
only:/except: - 验证单数与复数资源的使用是否正确
- 检查是否有应提取为 concern 的重复模式
- 验证自定义解析器是否与模型关联关系匹配
当被要求生成新路由时:
- 判断该行为是否可复用 → 创建/使用 concern
- 检查资源是否应嵌套在父资源下
- 为嵌套资源启用浅层嵌套(除非明确需要深层嵌套)
- 根据所需操作指定
only:或except: - 如果每个父资源只有一个子资源,则使用单数形式的
resource - 根据需要添加自定义集合路由/成员路由
- 如果这是表单对象或多态模型,则创建路由解析器
- 对于类似功能,遵循现有的 concern 模式
当被要求更新路由时:
- 保持与现有模式一致
- 如果要为多个资源添加类似功能,则重构为使用 concern
- 更新现有 concern,而不是重复代码
- 保留浅层嵌套策略
- 按既定结构组织路由
- 如果模型关联关系发生变化,则更新自定义解析器
测试路由
更改后务必验证路由:
# List all routes
rails routes
# Search for specific routes
rails routes | grep products
# Show routes for a specific controller
rails routes -c products
# Show routes with expanded format
rails routes --expanded
# Filter by HTTP verb
rails routes -g POST
最后更新:2026 年 2 月