ruby-on-rails 在after_action回调中检测动作邮件程序传递失败

hsgswve4  于 2023-04-22  发布在  Ruby
关注(0)|答案(4)|浏览(102)

我在我的邮件中使用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

我如何在我的mailer方法中捕获网络错误并记录电子邮件尝试失败,或者什么都不做?我使用的是Rails 4.2.4。

lnlaulya

lnlaulya1#

这是我想出来的,我想有一个更好的方法。
我使用了Mail delivery回调:

    • 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回调。
有更好的办法吗????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????

anhgbhbe

anhgbhbe2#

你所展示的调试消息非常有意义-mailer动作会立即完成,因为邮件动作本身是异步的,由Delayed job在一个完全不同的进程中处理。所以mailer类本身无法知道邮件动作是如何完成的。
我认为你需要的是实现延迟作业挂钩。你必须重写你的邮件和调用发送电子邮件一点。
我还没有完全测试过它,但沿着以下路线的东西应该可以工作:

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是一个由Delayed job运行的自定义作业。我试图使它尽可能通用,所以它接受mailer类,mailer操作,收件人(通常是用户)和其他可选参数。它还要求recipient具有emails关联。
该作业定义了两个挂钩:success,当邮件操作成功时,在数据库中创建email记录,并在日志记录失败时创建另一个记录。实际发送在perform方法中完成。注意,在perform方法中,没有使用delayed方法,因为整个作业在被调用时已经在后台延迟作业队列中排队。
要使用此自定义作业发送邮件,您必须将其入队到延迟作业,例如:

Delayed::Job.enqueue MailerJob.new(UserMailer, :spam!, User.find(1))
xkftehaa

xkftehaa3#

成功发送后的回调被称为observers in action mailer,从rails 3.x开始可用。
来自https://guides.rubyonrails.org/action_mailer_basics.html#observing-emails:
Observer类必须实现:delivered_email(message)方法,该方法将在电子邮件发送后调用。

class EmailDeliveryObserver
  def self.delivered_email(message)
    EmailDelivery.log(message)
  end
end

与拦截器类似,你必须使用observers配置选项注册观察者。你可以在初始化器文件(如config/initializers/mail_observers.rb)中注册:

Rails.application.configure do
  config.action_mailer.observers = %w[EmailDeliveryObserver]
end

还有一个register_observer类方法,用于仅向特定邮件程序添加观察者。

czq61nw1

czq61nw14#

尝试以下操作:

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

相关问题