ActionMailer не может найти reset_token, сообщает об отсутствии ключа :id

Я использую Delayed Job в качестве ActiveJob серверной части очереди и пытаюсь отправлять электронные письма, используя Метод deliver_later ActionMailer. Я считаю, что у меня все правильно настроено для отложенной работы, и я запускаю фонового работника на своем компьютере для разработки.

Когда я отправляю электронное письмо для сброса пароля, я получаю следующую ошибку:

[Рабочий (хост: компьютер pid: 7240)] Задание ActiveJob::QueueAdapters::DelayedJobAdapter::JobWrapper (id=1) FAILED (5 предыдущих попыток) с ActionView::Template::Error: Нет соответствий маршрута {:action=> "edit", :controller=>"password_resets", :email=>"[email protected]", :id=> nil} отсутствуют необходимые ключи: [:id]

Вот как я отправляю электронное письмо для сброса пароля. Это находится в моей модели User:

def send_password_reset_email
  UserMailer.password_reset(self).deliver_later
end

Моя настройка сброса пароля очень похожа на настройку в этой записи SO в том, что я не храню свой reset_token в базе данных, а вместо этого использую его как виртуальный атрибут, и я думаю, что это может быть моей проблемой, но я хочу по возможности избегать сохранения этого значения. Есть ли способ передать сгенерированный reset_token работнику отложенной работы? Также возможно, что моя проблема связана с чем-то другим.

Любая помощь будет принята с благодарностью!


person Alexander    schedule 09.01.2016    source источник


Ответы (2)


Я понял! У меня всегда был ответ; Мне пришлось хранить reset_token в базе данных. Я скопирую ответ из этого сообщения о переполнении стека< /а> ниже. Ответ принадлежит компании sevenseacat.

Когда вы не используете воркеров, вы сохраняете reset_token в экземпляре пользователя, а затем передаете тот же самый экземпляр пользователя в свою почтовую программу, поэтому reset_token все еще доступен.

Когда вы используете воркеров, ваш воркер имеет только идентификатор пользователя, поэтому он перезагружает экземпляр пользователя из базы данных. Поскольку reset_token не хранится в базе данных, он возвращается к нулю.

Либо вы должны сохранить reset_token в базе данных, либо ваш адрес электронной почты с паролем должен использовать reset_digest в URL-адресе.

После того, как я изменил свой reset_token с виртуального атрибута на столбец базы данных, проблема была решена. Мои электронные письма для сброса пароля теперь рассылаются.

ИЗМЕНИТЬ (18 января 2016 г.)

Я хотел добавить немного дополнительной информации, объясняющей, почему reset_token решил проблему, хотя в сообщении об ошибке утверждалось, что id отсутствует. В своем электронном письме для сброса пароля я генерирую URL-адрес действия для сброса пароля edit следующим образом:

<%= edit_password_resets_path(@user.reset_token) %>

Маршрут для моего действия по изменению пароля выглядит следующим образом:

edit_password_resets   GET   /password_resets/:id/edit

При создании URL-адреса первый указанный параметр заполняет сегмент :id URL-адреса. В моем случае @user.reset_token заполнялось вместо id, в результате чего сгенерированный URL-адрес был /password_resets/{reset token here}/edit. Когда асинхронное задание пыталось сгенерировать URL-адрес, оно ожидало указать значение для сегмента id URL-адреса. Я вставил свой reset_token вместо id, и, поскольку reset_token был виртуальным атрибутом и был равен nil, когда ActiveJob запустился, он выдал ошибку, поскольку в нем отсутствовало значение.

person Alexander    schedule 16.01.2016

Я просто пытался решить похожую проблему сам и пришел к немного другому решению. Чтобы не хранить токен сброса в базе данных, вы можете реорганизовать свой UserMailer.password_reset(self), чтобы он принимал два параметра UserMailer.password_reset(self, self.reset_token). Затем создайте две переменные экземпляра для передачи в шаблон почтовой программы:

@user = user
@reset_token = reset_token

и, наконец, в самом шаблоне почтовой программы вы можете просто сделать:

<%= edit_password_resets_path(@reset_token) %>`
person cconrad    schedule 04.03.2017
comment
Спасибо, что поделились своим решением. Вскоре после того, как я написал свое решение, я тоже пошел по пути, подобному этому. Я создал отдельное задание и передал токен сброса пароля в полезную нагрузку задания следующим образом: Этот код вызывается в модели User. SendPasswordResetJob.perform_later(self.id, self.password_reset_token) - person Alexander; 05.03.2017