在 after_action 回调中检测操作邮件发送失败
Detect action mailer delivery failures in after_action callbacks
我在我的邮件程序中使用 after_action
回调来记录电子邮件已发送。电子邮件通过延迟作业发送。这有效,除非我们无法连接到远程服务器 - 在这种情况下,电子邮件不会发送,但我们会记录下来。延迟作业稍后重试电子邮件,并成功发送,但我们随后记录了已发送两封电子邮件。
看起来像这样:
class UserMailer < ActionMailer::Base
after_action :record_email
def record_email
Rails.logger.info("XYZZY: Recording Email")
@user.emails.create!
end
def spam!(user)
@user = user
Rails.logger.info("XYZZY: Sending spam!")
m = mail(to: user.email, subject: 'SPAM!')
Rails.logger.info("XYZZY: mail method finished")
m
end
end
我这样调用这段代码(使用 delayed job performable mailer):
UserMailer.delay.spam!( User.find(1))
当我在调试器中逐步执行此操作时,我的 after_action 方法似乎在邮件投递之前被调用。
[Job:104580969] XYZZY: Sending spam!
[Job:104580969] XYZZY: mail method finished
[Job:104580969] XYZZY: Recording Email
Job UserMailer.app_registration_welcome (id=104580969) FAILED (3 prior attempts) with Errno::ECONNREFUSED: Connection refused - connect(2) for "localhost" port 1025
如何在我的邮件程序方法中捕获网络错误并记录电子邮件尝试失败,或者什么都不做?我正在使用 Rails 4.2.4.
尝试以下操作:
class UserMailer < ActionMailer::Base
# after_action :record_email
def record_email
Rails.logger.info("XYZZY: Recording Email")
@user.emails.create!
end
def spam!(user)
begin
@user = user
Rails.logger.info("XYZZY: Sending spam!")
m = mail(to: user.email, subject: 'SPAM!')
Rails.logger.info("XYZZY: mail method finished")
m
rescue Errno::ECONNREFUSED
record_email
end
end
end
这是我想出来的,我希望有更好的方法。
我使用了邮件投递回调:
delivery_callback.rb
class DeliveryCallback
def delivered_email(mail)
data = mail.instance_variable_get(:@_callback_data)
unless data.nil?
data[:user].email.create!
end
end
end
config/initializes/mail.rb
Mail.register_observer( DeliveryCallback.new )
我替换了我的 record_email 方法:
class UserMailer < ActionMailer::Base
after_action :record_email
def record_email
@_message.instance_variable_set(:@_callback_data, {:user => user})
end
end
这似乎可行,如果远程服务器不可用,则不会调用 delivered_email 回调。
有没有更好的方法!?!?
您显示的调试消息非常有意义 - 邮件操作立即完成,因为邮件操作本身是 异步的 ,由延迟作业在完全不同的进程中处理。所以邮寄者 class 本身无法知道邮寄操作是如何完成的。
我认为您需要的是实施 Delayed job hooks。不过,您必须稍微重写邮件程序和发送电子邮件的调用。
我还没有对其进行全面测试,但按照以下几行应该可以工作:
class MailerJob
def initialize(mailer_class, mailer_action, recipient, *params)
@mailer_class = mailer_class
@mailer_action = mailer_action
@recipient = recipient
@params = params
end
def perform
@mailer_class.send(@mailer_action, @recipient, *@params)
end
def success(job)
Rails.logger.debug "recording email!"
@recipient.emails.create!
end
def failure(job)
Rails.logger.debug "sending email to #{@recipient.email} failed!"
end
end
MailerJob
是 custom job 被延迟作业 运行。我试图让它尽可能通用,所以它接受邮件程序 class、邮件程序操作、收件人(通常是用户)和其他可选参数。它还要求 recipient
具有 emails
关联。
该作业定义了两个挂钩:success
当邮件操作成功时在数据库中创建 email
记录,另一个用于记录失败。实际发送是在 perform
方法中完成的。请注意,在其中 delayed
方法未被使用,因为整个作业在被调用时已经在后台延迟作业队列中排队。
要使用此自定义作业发送邮件,您必须将其加入延迟作业,例如:
Delayed::Job.enqueue MailerJob.new(UserMailer, :spam!, User.find(1))
我在我的邮件程序中使用 after_action
回调来记录电子邮件已发送。电子邮件通过延迟作业发送。这有效,除非我们无法连接到远程服务器 - 在这种情况下,电子邮件不会发送,但我们会记录下来。延迟作业稍后重试电子邮件,并成功发送,但我们随后记录了已发送两封电子邮件。
看起来像这样:
class UserMailer < ActionMailer::Base
after_action :record_email
def record_email
Rails.logger.info("XYZZY: Recording Email")
@user.emails.create!
end
def spam!(user)
@user = user
Rails.logger.info("XYZZY: Sending spam!")
m = mail(to: user.email, subject: 'SPAM!')
Rails.logger.info("XYZZY: mail method finished")
m
end
end
我这样调用这段代码(使用 delayed job performable mailer):
UserMailer.delay.spam!( User.find(1))
当我在调试器中逐步执行此操作时,我的 after_action 方法似乎在邮件投递之前被调用。
[Job:104580969] XYZZY: Sending spam!
[Job:104580969] XYZZY: mail method finished
[Job:104580969] XYZZY: Recording Email
Job UserMailer.app_registration_welcome (id=104580969) FAILED (3 prior attempts) with Errno::ECONNREFUSED: Connection refused - connect(2) for "localhost" port 1025
如何在我的邮件程序方法中捕获网络错误并记录电子邮件尝试失败,或者什么都不做?我正在使用 Rails 4.2.4.
尝试以下操作:
class UserMailer < ActionMailer::Base
# after_action :record_email
def record_email
Rails.logger.info("XYZZY: Recording Email")
@user.emails.create!
end
def spam!(user)
begin
@user = user
Rails.logger.info("XYZZY: Sending spam!")
m = mail(to: user.email, subject: 'SPAM!')
Rails.logger.info("XYZZY: mail method finished")
m
rescue Errno::ECONNREFUSED
record_email
end
end
end
这是我想出来的,我希望有更好的方法。
我使用了邮件投递回调:
delivery_callback.rb
class DeliveryCallback
def delivered_email(mail)
data = mail.instance_variable_get(:@_callback_data)
unless data.nil?
data[:user].email.create!
end
end
end
config/initializes/mail.rb
Mail.register_observer( DeliveryCallback.new )
我替换了我的 record_email 方法:
class UserMailer < ActionMailer::Base
after_action :record_email
def record_email
@_message.instance_variable_set(:@_callback_data, {:user => user})
end
end
这似乎可行,如果远程服务器不可用,则不会调用 delivered_email 回调。
有没有更好的方法!?!?
您显示的调试消息非常有意义 - 邮件操作立即完成,因为邮件操作本身是 异步的 ,由延迟作业在完全不同的进程中处理。所以邮寄者 class 本身无法知道邮寄操作是如何完成的。
我认为您需要的是实施 Delayed job hooks。不过,您必须稍微重写邮件程序和发送电子邮件的调用。
我还没有对其进行全面测试,但按照以下几行应该可以工作:
class MailerJob
def initialize(mailer_class, mailer_action, recipient, *params)
@mailer_class = mailer_class
@mailer_action = mailer_action
@recipient = recipient
@params = params
end
def perform
@mailer_class.send(@mailer_action, @recipient, *@params)
end
def success(job)
Rails.logger.debug "recording email!"
@recipient.emails.create!
end
def failure(job)
Rails.logger.debug "sending email to #{@recipient.email} failed!"
end
end
MailerJob
是 custom job 被延迟作业 运行。我试图让它尽可能通用,所以它接受邮件程序 class、邮件程序操作、收件人(通常是用户)和其他可选参数。它还要求 recipient
具有 emails
关联。
该作业定义了两个挂钩:success
当邮件操作成功时在数据库中创建 email
记录,另一个用于记录失败。实际发送是在 perform
方法中完成的。请注意,在其中 delayed
方法未被使用,因为整个作业在被调用时已经在后台延迟作业队列中排队。
要使用此自定义作业发送邮件,您必须将其加入延迟作业,例如:
Delayed::Job.enqueue MailerJob.new(UserMailer, :spam!, User.find(1))