CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
Ruby on Rails 实战入门/课时/路由设计
RawBlame
课时目录12 节

Ruby on Rails 实战入门

  1. 01Rails 概览与项目结构
  2. 02路由设计
  3. 03控制器
  4. 04Active Record 模型
  5. 05查询与优化
  6. 06数据库适配
  7. 07视图与组件
  8. 08表单
  9. 09认证与授权
  10. 10资产与后台任务
  11. 11测试与调试
  12. 12部署与运维

路由设计

RESTful 与嵌套路由。

30 分钟

路由设计

这一课你将能做到

  1. 用 resources 一行声明完整的 RESTful 七路由,并用 bin/rails routes 验证
  2. 设计嵌套路由表达「评论属于文章」这类从属关系
  3. 用命名路由 helper(article_path)在代码里生成 URL,而不是手写字符串

为什么要学

路由是应用的「门面」:URL 设计得好,用户能猜到地址、搜索引擎友好、团队协作时接口语义清晰。Rails 的路由不只是 URL 映射表,它还反向生成 URL helper——你在视图里写 article_path(@article),重构 URL 结构时只改 routes.rb 一处,全站链接自动跟随。

对比本站:Spring Boot 里路由散落在每个 @GetMapping("/articles/{id}") 注解上;Rails 则集中在 config/routes.rb 一个文件里,整张路由表一目了然。两种风格各有拥趸,但集中式的可审计性在大项目里优势明显。

先建立图像

resources :articles 一行展开成的七条标准路由:

HTTP 动词路径控制器动作用途命名 helper
GET/articlesindex列表页articles_path
GET/articles/newnew新建表单页new_article_path
POST/articlescreate处理创建提交articles_path
GET/articles/:idshow详情页article_path(id)
GET/articles/:id/editedit编辑表单页edit_article_path(id)
PATCH/PUT/articles/:idupdate处理更新提交article_path(id)
DELETE/articles/:iddestroy删除article_path(id)

请求匹配流程:

GET /articles/5
     │
     ▼
routes.rb 自上而下逐条匹配
     │
     ▼
命中 get "/articles/:id", to: "articles#show"
     │
     ▼
params[:id] = "5"  ──►  ArticlesController#show

跟着做

步骤 1:声明资源路由并查看路由表

编辑 config/routes.rb:

Rails.application.routes.draw do
  root "articles#index"        # 首页
  resources :articles
end

查看生成的路由表:

bin/rails routes

输出(节选):

     Prefix Verb   URI Pattern                Controller#Action
       root GET    /                          articles#index
   articles GET    /articles(.:format)        articles#index
            POST   /articles(.:format)        articles#create
new_article GET    /articles/new(.:format)    articles#new
    article GET    /articles/:id(.:format)    articles#show
            PATCH  /articles/:id(.:format)    articles#update
            DELETE /articles/:id(.:format)    articles#destroy

步骤 2:限制只暴露需要的动作

如果文章不允许删除,用 except 收窄:

resources :articles, except: [:destroy]

或者只开放读接口:

resources :articles, only: [:index, :show]

原则:只暴露用到的路由,路由表就是你的攻击面清单。

步骤 3:嵌套路由表达从属关系

评论必须属于某篇文章:

Rails.application.routes.draw do
  root "articles#index"
  resources :articles do
    resources :comments, only: [:create, :destroy]
  end
end

生成的路径形如 /articles/5/comments,控制器里 params[:article_id] 自动可用:

# app/controllers/comments_controller.rb
class CommentsController < ApplicationController
  def create
    @article = Article.find(params[:article_id])
    @comment = @article.comments.create(comment_params)
    redirect_to article_path(@article)
  end

  private

  def comment_params
    params.require(:comment).permit(:commenter, :body)
  end
end

嵌套不要超过一层。两层以上(/a/1/b/2/c/3)URL 和控制器都会变得难维护,这时候用浅层嵌套(shallow: true)或扁平化设计。

步骤 4:自定义成员路由与集合路由

业务里常有超出标准七动作的需求,比如「发布文章」:

resources :articles do
  member do
    patch :publish      # PATCH /articles/:id/publish
  end
  collection do
    get :archived       # GET /articles/archived
  end
end
  • member:作用于单条记录,URL 带 :id
  • collection:作用于整个集合,URL 不带 id

步骤 5:在视图和控制台里使用命名 helper

<%# app/views/articles/index.html.erb %>
<%= link_to "写文章", new_article_path %>
<%= link_to article.title, article_path(article) %>
<%= button_to "删除", article_path(article), method: :delete %>

在 bin/rails console 里验证:

app.article_path(3)        # => "/articles/3"
app.articles_url           # => "http://www.example.com/articles"
app.publish_article_path(7) # => "/articles/7/publish"

_path 生成相对路径,_url 生成带域名的完整 URL(邮件里必须用 _url)。

讲清楚要点

  1. RESTful 是资源思维:URL 描述「资源」(名词),HTTP 动词描述「操作」。/articles/5 + DELETE 就是删除 5 号文章,不需要 /articles/delete/5 这种把动词塞进 URL 的写法。
  2. routes.rb 自上而下匹配,先命中先生效:通配路由(get "*path")必须放最后,否则会拦截后面的具体路由。
  3. :id 段进 params:路径里的 :id、:article_id 占位符都会进入 params 哈希,控制器直接取用。
  4. 命名 helper 是路由的反向收益:永远不要手写 "/articles/#{id}",用 article_path(id),URL 结构变更时代码零改动。
  5. 重定向与约束:get "/old", to: redirect("/new") 处理老链接;constraints 可按 IP、子域名限制路由,后台管理路由常用。

你可能会懵的地方

  • resources 和 resource(单数)区别? 单数 resource :profile 表示「用户只有一个资料」,路径是 /profile(无 id),helper 是 profile_path。适合「当前用户的设置」这类场景。

  • PATCH 和 PUT 都指向 update,用哪个? 语义上 PATCH 是部分更新、PUT 是整体替换,Rails 里二者完全等价地路由到 update 动作,实际开发用 PATCH 即可。

  • HTML 表单只支持 GET/POST,DELETE 请求怎么发的? Rails 通过隐藏字段 _method=delete + JavaScript(data-turbo-method)模拟,服务端 Rack 中间件把它还原成真正的 DELETE 语义。

  • .:format 是什么? 路径后缀格式,如 /articles.json。控制器里用 respond_to 按格式返回 HTML 或 JSON,是 Rails 一套代码同时服务页面和 API 的经典手法。

  • 路由文件太大怎么办? 用 draw :admin 把路由拆到 config/routes/admin.rb,Rails 6.1+ 原生支持。

错法对照

错法问题正解
get "/articles/delete/:id"动词塞进 URL,GET 改变数据,违反 REST 与安全语义resources :articles,用 DELETE 动词
视图里手写 href="/articles/#{a.id}"URL 变更时全站改article_path(a)
三层嵌套 /a/1/b/2/c/3URL 丑陋,控制器要找三个父对象最多嵌一层,或用 shallow: true
通配路由放文件顶部拦截后续所有具体路由get "*path" 永远放最后
每个动作都手写 get/post路由表冗长易漏resources + only/except

卡点急救

  1. 访问页面报 Routing Error: No route matches:跑 bin/rails routes 确认路径和动词;在报错页直接用浏览器顶部搜索框过滤路由。
  2. undefined method 'xxx_path':helper 名由 Prefix 列决定,bin/rails routes | grep xxx 查正确前缀;注意单复数。
  3. 嵌套路由里 params[:article_id] 是 nil:检查 routes.rb 里 resources :comments 是否真正写在 resources :articles do ... end 块内。

面试常问

  • RESTful 路由的七个标准动作是什么? 答:index(列表)、show(详情)、new(新建表单)、create(创建)、edit(编辑表单)、update(更新)、destroy(删除)。对应 GET/POST/PATCH/DELETE 四个动词,URL 只含名词。

  • 嵌套路由什么时候用,有什么限制? 答:当子资源离开父资源没有独立意义时用(评论离不开文章)。限制:最多嵌一层,否则 URL 冗长、控制器链式查找父对象成本高;深层关系用浅层嵌套或在子资源路由里传父 id 参数。

  • Rails 路由相比注解式路由(Spring 的 @RequestMapping)的优劣? 答:集中式(routes.rb)优点是全局可审计、rails routes 一条命令看全貌、反向生成 helper;缺点是路由与控制器物理分离,新人要跳两个文件。注解式优点是路由贴着代码、直观;缺点是分散在几十个类里,全局接口清单需要工具扫描才能拿到。

自测三问

  1. resources :articles, only: [:index, :show] 会生成哪几条路由?各自的 helper 名是什么?
  2. 为什么 /articles/archived 要用 collection 而不是 member?
  3. 在视图中生成文章详情链接,正确写法是什么?为什么不能手写字符串?

小结一句话

路由是 URL 与控制器之间的契约:resources 给你 RESTful 标准答案,命名 helper 让全站链接可重构。

延伸

  • Rails Routing from the Outside In(官方路由指南)
  • 下一课:控制器——接住路由分发来的请求,协调模型与视图。
加载本节练习…
← 上一课Rails 概览与项目结构下一课 →控制器