Rails RESTful路由 #
一、REST架构概述 #
1.1 什么是REST #
REST(Representational State Transfer)是一种软件架构风格,用于设计网络应用。Rails完美支持RESTful设计。
1.2 REST原则 #
| 原则 | 说明 |
|---|---|
| 资源标识 | 每个资源有唯一URI |
| 统一接口 | 使用标准HTTP方法 |
| 无状态 | 请求之间无依赖 |
| 分层系统 | 可分层架构 |
| 表现层分离 | 资源与表现分离 |
1.3 HTTP方法与CRUD #
| HTTP方法 | CRUD操作 | 说明 |
|---|---|---|
| GET | Read | 获取资源 |
| POST | Create | 创建资源 |
| PUT/PATCH | Update | 更新资源 |
| DELETE | Delete | 删除资源 |
二、资源路由详解 #
2.1 完整资源路由 #
ruby
# config/routes.rb
Rails.application.routes.draw do
resources :articles
end
2.2 生成的路由详解 #
| 路径助手 | HTTP方法 | 路径 | 动作 | 用途 |
|---|---|---|---|---|
articles_path |
GET | /articles | index | 文章列表 |
new_article_path |
GET | /articles/new | new | 新建表单 |
articles_path |
POST | /articles | create | 创建文章 |
article_path(id) |
GET | /articles/:id | show | 文章详情 |
edit_article_path(id) |
GET | /articles/:id/edit | edit | 编辑表单 |
article_path(id) |
PATCH | /articles/:id | update | 更新文章 |
article_path(id) |
PUT | /articles/:id | update | 更新文章 |
article_path(id) |
DELETE | /articles/:id | destroy | 删除文章 |
2.3 控制器实现 #
ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
before_action :set_article, only: %i[show edit update destroy]
# GET /articles
def index
@articles = Article.all
end
# GET /articles/1
def show
end
# GET /articles/new
def new
@article = Article.new
end
# GET /articles/1/edit
def edit
end
# POST /articles
def create
@article = Article.new(article_params)
if @article.save
redirect_to @article, notice: '文章创建成功'
else
render :new, status: :unprocessable_entity
end
end
# PATCH/PUT /articles/1
def update
if @article.update(article_params)
redirect_to @article, notice: '文章更新成功'
else
render :edit, status: :unprocessable_entity
end
end
# DELETE /articles/1
def destroy
@article.destroy
redirect_to articles_url, notice: '文章删除成功'
end
private
def set_article
@article = Article.find(params[:id])
end
def article_params
params.require(:article).permit(:title, :body, :status)
end
end
三、资源路由选项 #
3.1 限制动作 #
ruby
# config/routes.rb
Rails.application.routes.draw do
# 只生成指定动作
resources :articles, only: [:index, :show]
# 排除指定动作
resources :comments, except: [:new, :edit]
# 只读资源
resources :pages, only: [:show]
end
3.2 自定义路径 #
ruby
# config/routes.rb
Rails.application.routes.draw do
# 自定义资源路径
resources :articles, path: 'posts'
# /posts => articles#index
# 自定义路径助手前缀
resources :articles, as: 'posts'
# posts_path => /articles
# 自定义控制器
resources :articles, controller: 'posts'
# 使用 PostsController
end
3.3 自定义成员和集合 #
ruby
# config/routes.rb
Rails.application.routes.draw do
resources :articles do
# 成员路由(操作单个资源)
member do
get 'preview' # /articles/:id/preview
post 'publish' # /articles/:id/publish
post 'unpublish' # /articles/:id/unpublish
end
# 集合路由(操作资源集合)
collection do
get 'search' # /articles/search
get 'popular' # /articles/popular
get 'archived' # /articles/archived
end
end
end
3.4 路径助手 #
erb
<!-- 成员路由 -->
<%= link_to '预览', preview_article_path(@article) %>
<%= button_to '发布', publish_article_path(@article), method: :post %>
<!-- 集合路由 -->
<%= link_to '搜索', search_articles_path %>
<%= link_to '热门文章', popular_articles_path %>
四、嵌套资源 #
4.1 基本嵌套 #
ruby
# config/routes.rb
Rails.application.routes.draw do
resources :articles do
resources :comments
end
end
4.2 嵌套路由详解 #
| 路径助手 | HTTP方法 | 路径 | 动作 |
|---|---|---|---|
article_comments_path(@article) |
GET | /articles/:article_id/comments | index |
new_article_comment_path(@article) |
GET | /articles/:article_id/comments/new | new |
article_comments_path(@article) |
POST | /articles/:article_id/comments | create |
article_comment_path(@article, @comment) |
GET | /articles/:article_id/comments/:id | show |
edit_article_comment_path(@article, @comment) |
GET | /articles/:article_id/comments/:id/edit | edit |
article_comment_path(@article, @comment) |
PATCH | /articles/:article_id/comments/:id | update |
article_comment_path(@article, @comment) |
DELETE | /articles/:article_id/comments/:id | destroy |
4.3 控制器实现 #
ruby
# app/controllers/comments_controller.rb
class CommentsController < ApplicationController
before_action :set_article
# GET /articles/:article_id/comments
def index
@comments = @article.comments
end
# POST /articles/:article_id/comments
def create
@comment = @article.comments.build(comment_params)
if @comment.save
redirect_to @article, notice: '评论创建成功'
else
redirect_to @article, alert: '评论创建失败'
end
end
private
def set_article
@article = Article.find(params[:article_id])
end
def comment_params
params.require(:comment).permit(:body, :author)
end
end
4.4 浅层嵌套 #
ruby
# config/routes.rb
Rails.application.routes.draw do
# 浅层嵌套避免URL过深
resources :articles do
resources :comments, shallow: true
end
end
# 生成的路由:
# GET /articles/:article_id/comments comments#index
# GET /articles/:article_id/comments/new comments#new
# POST /articles/:article_id/comments comments#create
# GET /comments/:id comments#show
# GET /comments/:id/edit comments#edit
# PATCH /comments/:id comments#update
# DELETE /comments/:id comments#destroy
4.5 深度嵌套(不推荐) #
ruby
# config/routes.rb
Rails.application.routes.draw do
# 避免超过一层嵌套
resources :users do
resources :articles do
resources :comments
end
end
end
# 更好的做法:使用浅层嵌套
resources :users do
resources :articles, only: [:index, :new, :create]
end
resources :articles, only: [:show, :edit, :update, :destroy] do
resources :comments, shallow: true
end
五、命名空间 #
5.1 基本命名空间 #
ruby
# config/routes.rb
Rails.application.routes.draw do
namespace :admin do
resources :users
resources :articles
root 'dashboard#index'
end
end
5.2 生成的路由 #
| 路径助手 | HTTP方法 | 路径 | 控制器 |
|---|---|---|---|
admin_users_path |
GET | /admin/users | admin/users#index |
new_admin_user_path |
GET | /admin/users/new | admin/users#new |
admin_users_path |
POST | /admin/users | admin/users#create |
admin_user_path(id) |
GET | /admin/users/:id | admin/users#show |
admin_root_path |
GET | /admin | admin/dashboard#index |
5.3 目录结构 #
text
app/controllers/
├── application_controller.rb
└── admin/
├── application_controller.rb
├── dashboard_controller.rb
├── users_controller.rb
└── articles_controller.rb
app/views/admin/
├── dashboard/
│ └── index.html.erb
├── users/
│ ├── index.html.erb
│ └── show.html.erb
└── articles/
└── index.html.erb
5.4 控制器实现 #
ruby
# app/controllers/admin/application_controller.rb
module Admin
class ApplicationController < ::ApplicationController
before_action :require_admin
private
def require_admin
redirect_to root_path unless current_user&.admin?
end
end
end
# app/controllers/admin/users_controller.rb
module Admin
class UsersController < Admin::ApplicationController
def index
@users = User.all
end
def show
@user = User.find(params[:id])
end
end
end
5.5 scope与namespace #
ruby
# config/routes.rb
Rails.application.routes.draw do
# namespace:路径、模块、助手前缀都改变
namespace :admin do
resources :users
end
# /admin/users => Admin::UsersController
# admin_users_path
# scope module:只改变模块
scope module: 'admin' do
resources :users
end
# /users => Admin::UsersController
# users_path
# scope path:只改变路径
scope path: '/admin' do
resources :users
end
# /admin/users => UsersController
# users_path
# scope as:只改变助手前缀
scope as: 'admin' do
resources :users
end
# /users => UsersController
# admin_users_path
end
六、API路由设计 #
6.1 API命名空间 #
ruby
# config/routes.rb
Rails.application.routes.draw do
namespace :api do
namespace :v1 do
resources :articles
resources :users, only: [:index, :show]
post 'login', to: 'authentication#login'
end
namespace :v2 do
resources :articles
resources :users
end
end
end
6.2 API控制器 #
ruby
# app/controllers/api/v1/articles_controller.rb
module Api
module V1
class ArticlesController < ApplicationController
skip_before_action :verify_authenticity_token
def index
@articles = Article.all
render json: @articles
end
def show
@article = Article.find(params[:id])
render json: @article
end
def create
@article = Article.new(article_params)
if @article.save
render json: @article, status: :created
else
render json: { errors: @article.errors }, status: :unprocessable_entity
end
end
private
def article_params
params.require(:article).permit(:title, :body)
end
end
end
end
6.3 API路由约束 #
ruby
# config/routes.rb
Rails.application.routes.draw do
# 子域名API
constraints subdomain: 'api' do
namespace :api, path: '/' do
namespace :v1 do
resources :articles
end
end
end
# 格式约束
namespace :api, defaults: { format: :json } do
namespace :v1 do
resources :articles
end
end
end
七、路由Concerns #
7.1 定义Concerns #
ruby
# config/routes.rb
Rails.application.routes.draw do
# 定义可复用的路由模块
concern :commentable do
resources :comments
end
concern :likable do
resources :likes, only: [:create, :destroy]
end
# 使用concerns
resources :articles, concerns: [:commentable, :likable]
resources :photos, concerns: [:commentable, :likable]
end
7.2 Concerns最佳实践 #
ruby
# config/routes.rb
Rails.application.routes.draw do
concern :paginatable do
get 'page/:page', action: :index, on: :collection
end
concern :searchable do
collection do
get 'search'
end
end
resources :articles, concerns: [:paginatable, :searchable]
resources :users, concerns: [:paginatable, :searchable]
end
八、路由重定向 #
8.1 基本重定向 #
ruby
# config/routes.rb
Rails.application.routes.draw do
# 简单重定向
get 'old-path', to: redirect('/new-path')
# 重定向到命名路由
get 'home', to: redirect(root_path)
# 外部重定向
get 'external', to: redirect('https://example.com')
end
8.2 动态重定向 #
ruby
# config/routes.rb
Rails.application.routes.draw do
# 使用块进行动态重定向
get 'users/:name', to: redirect { |params|
"/profiles/#{params[:name]}"
}
# 带状态码
get 'old-page', to: redirect('/new-page', status: 301)
end
九、路由最佳实践 #
9.1 路由组织 #
ruby
# config/routes.rb
Rails.application.routes.draw do
# 1. 根路由
root 'home#index'
# 2. 公开资源
resources :articles, only: [:index, :show]
resources :categories, only: [:index, :show]
# 3. 用户资源
resources :users, only: [:show] do
resources :articles, only: [:index]
end
# 4. 认证路由
get 'login', to: 'sessions#new'
post 'login', to: 'sessions#create'
delete 'logout', to: 'sessions#destroy'
# 5. 管理后台
namespace :admin do
root 'dashboard#index'
resources :users
resources :articles
end
# 6. API路由
namespace :api, defaults: { format: :json } do
namespace :v1 do
resources :articles
end
end
end
9.2 RESTful设计原则 #
| 原则 | 说明 |
|---|---|
| 使用资源名词 | /articles 而非 /article_list |
| 使用HTTP方法 | GET/POST/PUT/DELETE 而非 /delete_article |
| 嵌套不超过一层 | 避免深层嵌套 |
| 使用命名空间 | 组织相关路由 |
| 保持一致性 | 统一命名规范 |
9.3 路由命名规范 #
ruby
# 推荐
resources :articles
resources :users
# 不推荐
resources :article # 应该用复数
resources :User # 应该用小写
十、总结 #
10.1 核心要点 #
| 要点 | 说明 |
|---|---|
| RESTful | 使用资源和HTTP方法 |
| 资源路由 | resources :articles |
| 嵌套资源 | 一层嵌套最佳 |
| 命名空间 | 组织相关路由 |
| API设计 | 版本控制和格式约束 |
10.2 下一步 #
现在你已经掌握了RESTful路由,接下来让我们学习 路由约束,深入了解路由的高级约束条件!
最后更新:2026-03-28