Rails路由异常:user_path返回/user.:id而非/user/:id问题排查
我一眼就看出问题出在你的路由配置冲突上——你同时混合了自定义的单数路由和Rails标准的RESTful复数路由(resources :users),导致路由辅助方法命名混乱,最终让Rails把@user.id当成了格式参数(format),生成了/user.1这种异常路径。
具体问题分析
你的config/routes.rb里存在多个同名的user_path路由辅助方法:
post '/user' => 'users#create':生成POST方法的user_path,无:id参数post '/user/' => 'users#update':同样生成POST方法的user_path,覆盖前一个,仍无:id参数get '/user/:id' => 'users#show':生成GET方法的user_path,带:id参数resources :users:生成标准RESTful路由,其中GET/users/:id对应的辅助方法也是user_path,再次覆盖前面的定义
由于路由定义是从上到下解析,但辅助方法的命名会被后面的定义覆盖,而这里的混乱导致Rails最终错误地将user_path指向了无:id参数的POST路由。当你调用user_path(@user)时,Rails找不到带:id参数的匹配路由,就把@user.id当成了格式参数,于是生成了/user.1(格式参数用点号分隔)。
而edit_user_path正常,是因为resources :users生成的edit_user_path是唯一的,没有被自定义路由覆盖。
修复方案
你需要清理路由配置,避免自定义路由和RESTful路由的冲突,推荐两种方式:
方式一:仅使用标准RESTful路由(推荐)
移除所有自定义的用户CRUD路由,用resources :users生成标准路由,再通过额外的get路由定义自定义路径(如/signup、/profile),并指定唯一的辅助方法名:
Rails.application.routes.draw do root 'static#home' %w( 404 406 422 500 503 ).each do |code| get code, :to => "errors#show", :code => code end # 自定义页面路由,指定唯一命名避免冲突 get '/test' => 'users#test' get '/signup' => 'users#new', as: :signup get '/profile' => 'users#show', as: :profile get '/user/list' => 'users#index', as: :user_list # 标准RESTful用户路由,自动生成所有CRUD路由 resources :users end
这样:
user_path(@user)会生成标准的/users/:id路径signup_path对应/signup,profile_path对应/profile- 所有路由辅助方法都是唯一的,不会再出现冲突
方式二:使用自定义路径的RESTful路由(如果需要单数路径)
如果你确实需要/user/:id这种单数路径,可以修改resources的path选项,统一使用单数路径,同时移除所有自定义的用户路由:
Rails.application.routes.draw do root 'static#home' %w( 404 406 422 500 503 ).each do |code| get code, :to => "errors#show", :code => code end get '/test' => 'users#test' get '/signup' => 'users#new', as: :signup get '/profile' => 'users#show', as: :profile # 用path选项指定单数路径,自动生成所有CRUD路由 resources :users, path: '/user' end
此时user_path(@user)会生成/user/:id,符合你的预期,同时避免了路由冲突。
额外注意点
- 不要用POST方法处理
update动作,Rails标准是用PUT/PATCH,resources :users已经自动生成了对应的路由,不需要自定义。 - 路由定义要尽量遵循RESTful规范,减少自定义路由的使用,避免不必要的冲突。
内容的提问来源于stack exchange,提问作者Martin

