为什么 Devise 使用自定义路由创建不正确的丢失密码 POST 路径

Why does Devise creates incorrect lost password POST path with custom route

我有一个带有普通用户模型的简单 Devise 安装。我确认默认路由一切正常(注册、丢失密码等),然后开始像这样自定义:

  devise_for :users, :skip => [:sessions, :registrations, :passwords]
  as :user do

     get 'signin' => 'devise/sessions#new', :as => :new_user_session
     post 'signin' => 'devise/sessions#create', :as => :user_session
     delete 'signout' => 'devise/sessions#destroy', :as => :destroy_user_session

     get 'register/cancel' => 'devise/registrations#cancel', :as => :cancel_user_registration
     post 'register' => 'devise/registrations#create', :as => :user_registration
     get 'register' => 'devise/registrations#new', :as => :new_user_registration
     get 'account' => 'devise/registrations#edit', :as => :edit_user_registration
     patch 'account' => 'devise/registrations#update'
     put 'account' => 'devise/registrations#update'
     delete 'account' => 'devise/registrations#destroy'

     post 'password' => 'devise/passwords#create', :as => :user_password
     get 'password/forgot' => 'devise/passwords#new', :as => :new_user_password
     get 'password/reset' => 'devise/passwords#edit', :as  => :edit_user_password
     patch 'password' => 'devise/passwords#update'
     put 'password' => 'devise/passwords#update'

  end

然而,忘记密码页面会创建一个 POST 路径,如下所示:

<form class="new_user" id="new_user" action="/password.user" accept-charset="UTF-8" method="post">
...
</form>

从相应的观点来看:

<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %>

使用默认的 Devise 路由,url 正确显示为 /users/password,但使用我的自定义路由,我得到 /password.user。好像只有这一条路。

我知道简单的解决方法是将 password_path(resource_name) 更改为 password_path,但我想首先了解为什么会中断。

在深入研究 Devise 的源代码并进行一些试验之后,我想我已经找到了发生这种情况的原因。如果您查看 devise_for 为模型 :user 生成的路由,密码路由有一个前缀 user_password。路由方法是 user_password_path。那么 password_path 实际上是如何工作的呢?

new.html.erb中调用的password_path方法实际上并不是devise_for :user生成的方法。它是一个以模型名称作为参数的辅助方法。例如,当传递 :user 时,它将如下所示:

password_path(:user) => user_password_path

此方法然后调用 user_password_path,这是正确的方法。更多信息 here.

在你的例子中,你正在创建一个前缀为 user_password 的路由,但你也有这些路由:

 patch 'password' => 'devise/passwords#update'
 put 'password' => 'devise/passwords#update'

生成前缀为 password 的路由。我猜这个 overrides/takes 优先于 UrlHelper.

中的 password_path 方法

关于为什么 returns /password.user,每个路由方法都有一个可选的格式参数,指定请求的格式(例如 /password.html/password.json 等) .在您的情况下,resource_name 的值为 user.

你能做什么?我建议您将 putpatch 方法与 :edit_user_password 前缀相关联,保持与 devise 生成的路由相同的结构。这样你就不会 override/hide devise 的 UrlHelper 中定义的 password_path 方法。

注意:在试验路线时,我不得不在某些时候重新启动我的服务器。因此,如果某些事情没有按预期发生,请尝试重新启动它。