英文:
Ruby on Rails - Duplicate routes & impact on controllers
问题
- 这是正确的方法吗?还有没有更好的解决方法?
- 如果我采用这种方法,如何处理控制器操作?因为在一种情况下(2),我们将获得
params[:group_id]
,但在另一种情况下,我需要将它添加为strong_params
。是否可以使用if-else
条件来处理?(如果在strong_params
中没有[:group_id]
,则使用params[:group_id]
)
英文:
I am creating a Task Manager.
So, I have:
- A dashboard where I can do CRUD for tasks (and then I will assign them a group)
- A group section where I can go inside a group and do CRUD for tasks
Then my routes would look something like this:
Rails.application.routes.draw do
devise_for :users
namespace :api, defaults: { format: :json } do
namespace :v1 do
resources :groups, only: [ :index, :show, :create, :update, :destroy] do
member do
resources :tasks, only: [:index, :show, :create, :update, :destroy]
end
end
resources :tasks, only: [:index, :show, :create, :update, :destroy]
end
end
end
For this:
- A dashboard where I can do CRUD for tasks (and then I will assign them a group)
resources :tasks, only: [:index, :show, :create, :update, :destroy]
And for this:
- A group section where I can go inside a group and do CRUD for tasks
resources :groups, only: [ :index, :show, :create, :update, :destroy] do
member do
resources :tasks, only: [:index, :show, :create, :update, :destroy]
end
end
But I feel this is duplicating routes. I have 2 questions:
- Is this the right approach or would there be a better way to solve it?
- If I go with this, how would I approach the controller actions? Since in one situation (2) we would get the
params[:group_id]
but in the other I would need to add it asstrong_params
. Would anif-else
condition work? (if there is no[:group_id]
instrong_params
then take theparams[:group_id]
)
Thanks!
答案1
得分: 1
不是的。不太好。
深度嵌套的路由很快变得难以处理,因此您应该考虑使用浅层嵌套。这仅嵌套了集合路由(索引,新建,创建),而不嵌套成员路由(显示,编辑,更新,删除)。
如果任务具有唯一的ID,您应该能够在不涉及组的情况下显示,编辑,更新和删除它们。这个规则的例外情况是如果任务只在组内是唯一的(但为什么呢?)。
在API中,这尤其如此。后端不应该知道您是从前端应用程序的页面X或Y修改资源。
您可以使用 shallow: true
选项来生成浅层路由,但我可能会将其定义为:
# 这表示没有嵌套在组中的任务
resources :tasks, only: [:index, :show, :create, :update, :destroy]
resources :groups, only: [:index, :show, :create, :update, :destroy] do
resources :tasks, only: [:index, :create]
end
如果我采用这个方法,如何处理控制器操作呢?
这里实际上有两种不同的方法。其中一种我称之为 "参数嗅探方法":
module API
module V1
class TasksController
# GET /api/v1/groups/1/tasks
# GET /api/v1/tasks
def index
if params[:group_id]
render json: Group.find(params[:group_id]).tasks
else
render json: Task.all
end
end
end
end
end
这是最明显的解决方案,但明显违反了 单一职责原则 并增加了所有方法的循环复杂度。
另一种解决方案是将资源的嵌套表示路由到单独的控制器:
# 这表示不嵌套在组中的任务
resources :tasks, only: [:index, :show, :create, :update, :destroy]
resources :groups, only: [:index, :show, :create, :update, :destroy] do
resources :tasks, only: [:index, :create], module: :groups
end
这将 /groups/:group_id/tasks
路由到 API::V1::Groups::TasksController
。
module API
module V1
class TasksController
# GET /api/v1/tasks
def index
render json: Task.all
end
end
end
end
module API
module V1
module Groups
class TasksController
before_action :set_group
# GET /api/v1/groups/1/tasks
def index
render json: @group.tasks
end
private
def set_group
@group = Group.find(params[:group_id])
end
end
end
end
end
如果您想避免再嵌套一步,您也可以只使用 resources :tasks, only: [:index, :create], controller: :group_tasks
。
英文:
> Is this the right approach or would there be a better way to solve it?
No. Not really.
Deeply nested routes quickly become cumbersome to work with so you should consider using shallow nesting. This only nests the collection routes (index, new, create) and not the member routes (show, edit, update, destroy).
If tasks have a unique id you should be able to show, edit, update and destroy them without the group being involved. The exception to this rule is if tasks are only unique per group (but why?).
In an API this is especially true. The backend shouldn't need to know that you're modifying the resource from page X or Y in your frontend application.
You can use the shallow: true
option to generate shallow routes but I would probally just define it as:
# This respresents tasks not nested in a group
resources :tasks, only: [:index, :show, :create, :update, :destroy]
resources :groups, only: [:index, :show, :create, :update, :destroy] do
resources :tasks, only: [:index, :create]
end
> If I go with this, how would I approach the controller actions?
There are really two different approaches here. One I call the "param sniffing method":
module API
module V1
class TasksController
# GET /api/v1/groups/1/tasks
# GET /api/v1/tasks
def index
if params[:group_id]
render json: Group.find(params[:group_id]).tasks
else
render json: Task.all
end
end
end
end
end
This is the most obvious solution but blatantly violates the Single Responsibity Principle and increases the cyclic complexity of all the methods.
The other solution is to route the nested representation of the resource to a separate controller:
# This respresents tasks not nested in a group
resources :tasks, only: [:index, :show, :create, :update, :destroy]
resources :groups, only: [ :index, :show, :create, :update, :destroy] do
resources :tasks, only: [:index, :create], module: :groups
end
This routes the /groups/:group_id/tasks
routes into API::V1::Groups::TasksController
.
module API
module V1
class TasksController
# GET /api/v1/tasks
def index
render json: Task.all
end
end
end
end
module API
module V1
module Groups
class TasksController
before_action :set_group
# GET /api/v1/groups/1/tasks
def index
render json: @group.tasks
end
private
def set_group
@group = Group.find(params[:group_id])
end
end
end
end
end
You could also just do resources :tasks, only: [:index, :create], controller: :group_tasks
if you want to avoid nesting the contants one more step.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论