我在Rails 4.0.3应用程序上使用Devise 3.2.3
在我的用户模型中,我有一个after_create: subscribe
回调版,用于订阅新用户的简报。每次新用户尝试确认他们的电子邮件时,我都会介绍此回调,然后会收到confirmation token is invalid
条消息。然而,重发确认电子邮件的确认链接有效。
我显然可以避免使用:after_create
回调,但这非常痛苦。
User.rb:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :trackable, :validatable,
:confirmable, :rememberable
has_many :things
belongs_to :city
validates_presence_of :city_id
validates :email, :presence => true, :email => true
after_create :subscribe
def subscribe(frequency = :weekly)
if [:weekly, :monthly].include? frequency
response = Rails.configuration.mailchimp.lists.subscribe({
id: get_list_id(frequency),
email: { email: email },
merge_vars: { HASH: hashify(frequency), USER_ID: id }, # generate approptiate hash
double_optin: false
})
# response
end
update_attributes(newsletter_frequency: frequency.to_s)
response
end
end
答案 0 :(得分:1)
已经4个月了,我不知道你是否已经解决了这个问题,但我仍然会回答这个问题。
我昨天遇到了这个问题,我使用的是Devise
和Rails
版本。首先,我读取控制台日志,找到创建用户的时间,确认令牌有效,然后重写。
调试后,我在update
中发现问题为after create
。为什么?我发现update语句还添加了unconfirmed_email
属性,这意味着在更新时,电子邮件会发生变化。这很奇怪,因为我没有更新电子邮件专栏。所以我在更新语句
changed_attributes.each do |attr, origin|
puts "#{attr}: #{origin.inspect} => #{self.send(attr)}"
end
结果是:
id: nil => 108
email: "" => d33faf@qq.com
请参阅?更新时,电子邮件地址会更改,甚至创建id
,看起来还没有创建记录。这很奇怪,因为该记录应该被创建,因为它被称为after create
。
所有关于transaction
,create
操作都包含在整个事务中。 after create
仍然在此事务中,并且由于insert
语句尚未提交,因此Rails
认为数据库中的电子邮件为nil且电子邮件已更改。
所以到最后,更新会在Devise
中触发以下回调:
before_update :postpone_email_change_until_confirmation_and_regenerate_confirmation_token, if: :postpone_email_change?
然后它发现电子邮件已更改,因此它会重新生成确认令牌。
好的,我们怎么能避免呢?
最简单的方法是使用update_columns
代替update
,这不会触发before_update
回调,但会绕过验证。
更好的方法是使用after_commit on: :create
而不是after_create
,因为它将在事务后执行。但问题是目前它有一个bug。请参阅我在Github上创建的问题:https://github.com/rails/rails/issues/16286以及关于SO的问题:Prevent infinite loop when updating attributes within after_commit, :on => :create
答案 1 :(得分:0)
我认为这篇SO帖子可能会引导你朝着正确的方向前进。 Devise "Confirmation token is invalid" when user signs up
特别是此博文http://blog.plataformatec.com.br/2013/08/devise-3-1-now-with-more-secure-defaults/
答案 2 :(得分:0)
你需要使用'after_confirmation'方法而不是使用after_create回调。
def after_confirmation
#your code here
end