将多个路径与单个控制器操作匹配

时间:2017-04-24 14:05:18

标签: ruby-on-rails routing ruby-on-rails-5

在我的应用中,硬币可能属于也可能不属于网络。因此,我在routes.rb中有以下内容:

resources :coins

resources :networks do
    resources :coins
end

您可以通过硬币关注coins#关注并通过硬币取消关注#unfollow

这对于属于网络的硬币来说效果很好:

match 'networks/:network_id/coins/:id/follow', to: 'coins#follow', via: 'get', :as => :follow_coin

match 'networks/:network_id/coins/:id/unfollow', to: 'coins#unfollow', via: 'get', :as => :unfollow_coin

我也想这样做:

match 'coins/:id/follow', to: 'coins#follow', via: 'get', :as => :follow_coin

match '/coins/:id/unfollow', to: 'coins#unfollow', via: 'get', :as => :unfollow_coin

但访问coins/coinname时出现此错误,例如:

Invalid route name, already in use: 'follow_coin' You may have defined two routes with the same name using the `:as` option, or you may be overriding a route already defined by a resource with the same naming.

我不清楚适当的解决方案是什么。我想将networks/:network_id/coins/:id/followcoins/:id/follow映射到coins#follow操作,该操作已准备好处理属于网络的硬币和不属于网络的硬币。

我怀疑即使是:

match 'networks/:network_id/coins/:id/follow', to: 'coins#follow', via: 'get', :as => :follow_coin

我可以使用networks资源

更好地做到这一点

2 个答案:

答案 0 :(得分:1)

可以为不同的网址设置相同的controller#action

但它们应该有不同的route helper,因此在rails视图或控制器中,我们可以为它们生成相应的URL。

将代码更改为此

match 'networks/:network_id/coins/:id/follow', to: 'coins#follow', via: 'get', :as => :network_follow_coin
match 'coins/:id/follow', to: 'coins#follow', via: 'get', :as => :follow_coin

因此,在使用network_follow_coin(network_id, coin_id)时,会生成网址/networks/1/coins/2/follow

使用follow_coin_path(coin_id)时,会生成网址/coins/2/follow

至于简化代码,你应该使用resource来更好地包装和理解,减少match的使用(实际上我认为永远不会)。改变这种方式

resource :networks do
   resource :coins do
     member do
       put :follow
       put :unfollow
     end
   end
end

另一件事是,如果您在数据库中更改某些内容,则不应将http method设置为get。如果是get方法,则假设只获取数据,不进行修改。它也很危险,因为一些蜘蛛会访问您网站中的每个链接,它可能会激活一些不好的东西。

答案 1 :(得分:1)

除了seaify所说的(很棒的建议!),你可以这样做:

resource :coins do
  member do
    put :follow
    put :unfollow
  end
end

然后,当你想使用network_id时,做一些类似的事情(假设你定义了@coin变量):

follow_coin_path(network_id: network_id)

这会给你类似的东西:

/coins/2/follow?network_id=1

你的参数将是:

{..., id: 2, network_id: 1, ...}

因为听起来你已经有了确定network_id参数是否存在的逻辑。

如果您尝试使用coinname作为识别变量(而不是id),请执行以下操作:

resources :coins, param: :coinname do 
  member do 
    put :follow
    put :unfollow
  end
end

执行rake routes时,您会看到类似的内容:

   follow_coin PUT    /coins/:coinname/follow(.:format)        coins#follow
 unfollow_coin PUT    /coins/:coinname/unfollow(.:format)      coins#unfollow
         coins GET    /coins(.:format)                         coins#index
               POST   /coins(.:format)                         coins#create
      new_coin GET    /coins/new(.:format)                     coins#new
     edit_coin GET    /coins/:coinname/edit(.:format)          coins#edit
          coin GET    /coins/:coinname(.:format)               coins#show
               PATCH  /coins/:coinname(.:format)               coins#update
               PUT    /coins/:coinname(.:format)               coins#update
               DELETE /coins/:coinname(.:format)               coins#destroy

你的参数将包含类似的内容:

{..., coinname: 'bitcoin', ...}

此外,当您使用link_to时,请记住:

link_to("Follow #{@coin.name}", follow_coin_path(@coin), method: :put)

如果您不使用method: :put,则link_to会生成get。并且您最终会遇到No route matches [GET] "/coins/coinname/follow"错误。