Devise登出路由错误指向UsersController而非SessionsController求助
问题:Devise API登出请求被错误路由至UsersController
问题现象
登录功能正常,POST请求/api/users/sign_in能正确路由到API::SessionsController#create;但执行登出测试时,DELETE请求/api/users/sign_out被错误路由到API::UsersController,触发AbstractController::ActionNotFound错误(提示该控制器不存在destroy动作)。查看路由列表显示destroy_api_user_session应该指向api/sessions#destroy,同时项目中存在api/users的RESTful路由。
相关代码片段
测试登录代码
p "sign in URL: #{path + api_user_session_path}" post (path + api_user_session_path), params: { unlock: encrypted_unlock }
登录终端输出
"sign in URL: http://api.example.com/api/users/sign_in" Started POST "/api/users/sign_in" for 127.0.0.1 at 2023-12-06 15:36:51 -0500 (3.7ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC Processing by API::SessionsController#create as JSON
测试登出代码
p "sign out url: #{path + destroy_api_user_session_path}" delete (path + destroy_api_user_session_path)
登出终端报错
"sign out url: http://api.example.com/api/users/sign_out" Started DELETE "/api/users/sign_out" for 127.0.0.1 at 2023-12-06 15:36:51 -0500 AbstractController::ActionNotFound (The action 'destroy' could not be found for API::UsersController
routes.rb配置
Rails.application.routes.draw do namespace :api, defaults: { format: :json } do ... devise_for :users, controllers: { sessions: 'api/sessions' } ... end end
原因分析
核心问题是Rails路由的匹配顺序:Rails会按照路由文件中定义的顺序从上到下匹配请求。如果resources :users(生成RESTful路由,包含DELETE /api/users/:id)的定义在devise_for :users之前,那么当请求DELETE /api/users/sign_out时,Rails会把sign_out识别为:id参数,优先匹配到users#destroy路由,而不是Devise生成的session#destroy路由。
解决方案
方案1:调整路由定义顺序(推荐)
把devise_for :users的定义放在resources :users之前,让Devise的登出路由先被匹配:
Rails.application.routes.draw do namespace :api, defaults: { format: :json } do # 先定义Devise路由,确保优先匹配 devise_for :users, controllers: { sessions: 'api/sessions' } # 再定义其他RESTful路由 resources :users ... end end
方案2:添加路由约束
如果无法调整顺序,可以给resources :users添加约束,排除sign_out这类Devise路径,避免被误匹配:
Rails.application.routes.draw do namespace :api, defaults: { format: :json } do resources :users, constraints: { id: /\d+/ } do # 仅匹配数字ID,排除sign_out这类字符串 end devise_for :users, controllers: { sessions: 'api/sessions' } ... end end
内容的提问来源于stack exchange,提问作者Jeremy Thomas
相关产品推荐
相关产品推荐

