带有多个 SMTP 服务器的 Rails ActionMailer
Posted
技术标签:
【中文标题】带有多个 SMTP 服务器的 Rails ActionMailer【英文标题】:Rails ActionMailer with multiple SMTP servers 【发布时间】:2010-12-06 07:16:31 【问题描述】:我需要在 Rails 应用程序中使用两个不同的 smtp 服务器。看来ActionMailer的构造方式,不可能有不同的smtp_settings 一个子类。每当发送消息时,我都可以重新加载每个邮件程序类的 smtp 设置,但这会弄乱我无法控制的 ExceptionNotifier 插件(除非我也弄乱了它)。有没有人有类似的解决方案/插件 这个?
理想情况下我想拥有
class UserMailer < ActionMailer::Base; end
然后在environment.rb中设置
ActionMailer::Base.smtp_settings = standard_smtp_settings
UserMailer.smtp_settings = user_smtp_settings
因此,我的大多数邮件程序(包括 ExceptionNotifier)都会使用默认设置,但 UserMailer 会使用付费中继服务。
【问题讨论】:
更多参考,这里有一个拉取请求,最终只合并到 Rails 4,因为 Rails 3.2 不接受新功能。 github.com/rails/rails/pull/7397 查看@wnm 对 Rails 4 方式的回答,并附有文档链接。现在很简单。 ***.com/a/34948143/456791 【参考方案1】:根据 Oreilly 的文章,我想出了我在这里写的解决方案: http://transfs.com/devblog/2009/12/03/custom-smtp-settings-for-a-specific-actionmailer-subclass
以下是相关代码:
class MailerWithCustomSmtp < ActionMailer::Base
SMTP_SETTINGS =
:address => "smtp.gmail.com",
:port => 587,
:authentication => :plain,
:user_name => "custom_account@transfs.com",
:password => 'password',
def awesome_email(bidder, options=)
with_custom_smtp_settings do
subject 'Awesome Email D00D!'
recipients 'someone@test.com'
from 'custom_reply_to@transfs.com'
body 'Hope this works...'
end
end
# Override the deliver! method so that we can reset our custom smtp server settings
def deliver!(mail = @mail)
out = super
reset_smtp_settings if @_temp_smtp_settings
out
end
private
def with_custom_smtp_settings(&block)
@_temp_smtp_settings = @@smtp_settings
@@smtp_settings = SMTP_SETTINGS
yield
end
def reset_smtp_settings
@@smtp_settings = @_temp_smtp_settings
@_temp_smtp_settings = nil
end
end
【讨论】:
感谢您的回复 - 这看起来是一个很好的解决方案(我认为我没有足够的声誉来投票)。我最终从 ExceptionNotifier 切换到 HopToad,这使得 O'Reilly 解决方案再次成为可能。 我必须添加 :domain => 'localhost:3000', :authentication => :plain, :enable_starttls_auto => true 才能使其正常工作,但这是我找到的最佳解决方案 当然还要在生产环境中将 localhost:3000 更改为适当的域 但这似乎受到@jcalvert 对How to send emails with multiple, dynamic smtp using Actionmailer/Ruby on Rails 的回答中描述的并发问题的影响。【参考方案2】:class UserMailer < ActionMailer::Base
def welcome_email(user, company)
@user = user
@url = user_url(@user)
delivery_options = user_name: company.smtp_user,
password: company.smtp_password,
address: company.smtp_host
mail(to: @user.email,
subject: "Please see the Terms and Conditions attached",
delivery_method_options: delivery_options)
end
end
Rails 4 允许动态交付选项。上面的代码直接来自action mailer basics guide,你可以在这里找到:http://guides.rubyonrails.org/v4.0/action_mailer_basics.html#sending-emails-with-dynamic-delivery-options
这样,您可以为您发送的每封电子邮件设置不同的 smtp 设置,或者像在您的用例中为不同的子类(如 UserMailer、OtherMailer 等)设置不同的 smtp 设置。
【讨论】:
最佳答案。在 Rails 4 中完美运行:D 似乎使用回调也是一个可接受的选项...guides.rubyonrails.org/…:“使用 after_action 回调还可以让您通过更新 mail.delivery_method.settings 来覆盖传递方法设置。”考虑到您的交付设置很可能是它们所使用环境的函数,在真实场景中也可能更简洁【参考方案3】:Rails 4.2+ 的解决方案:
config/secrets.yml:
production:
gmail_smtp:
:authentication: "plain"
:address: "smtp.gmail.com"
:port: 587
:domain: "zzz.com"
:user_name: "zzz@zzz.com"
:password: "zzz"
:enable_starttls_auto: true
mandrill_smtp:
:authentication: "plain"
:address: "smtp.mandrillapp.com"
:port: 587
:domain: "zzz.com"
:user_name: "zzz@zzz.com"
:password: "zzz"
:enable_starttls_auto: true
mailgun_smtp:
:authentication: "plain"
:address: "smtp.mailgun.org"
:port: 587
:domain: "zzz.com"
:user_name: "zzz@zzz.com"
:password: "zzz"
:enable_starttls_auto: true
config/environments/production.rb:
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = Rails.application.secrets.gmail_smtp
app/mailers/application_mailer.rb:
class ApplicationMailer < ActionMailer::Base
default from: '"ZZZ" <zzz@zzz.com>'
private
def gmail_delivery
mail.delivery_method.settings = Rails.application.secrets.gmail_smtp
end
def mandrill_delivery
mail.delivery_method.settings = Rails.application.secrets.mandrill_smtp
end
def mailgun_delivery
mail.delivery_method.settings = Rails.application.secrets.mailgun_smtp
end
end
app/mailers/user_mailer.rb:
class UserMailer < ApplicationMailer
# after_action :gmail_delivery, only: [:notify_user]
after_action :mandrill_delivery, only: [:newsletter]
after_action :mailgun_delivery, only: [:newsletter2]
def newsletter(user_id); '...' end # this will be sent through mandrill smtp
def newsletter2(user_id); '...' end # this will be sent through mailgun smtp
def notify_user(user_id); '...' end # this will be sent through gmail smtp
end
【讨论】:
【参考方案4】:对于任何使用更高版本 (3+) 的 Rails 来解决此问题的人,试试这个
http://guides.rubyonrails.org/action_mailer_basics.html#sending-emails-with-dynamic-delivery-options
【讨论】:
这是最干净的解决方案!【参考方案5】:尝试在 Rails 3.2.1 中使用 jkrall 的选项,但由于某种原因它不会覆盖默认配置,而是这样做:
MyMailer.my_email.delivery_method.settings.merge!(SMTP_SETTINGS).deliver
类似于http://www.scottw.com/multiple-smtp-servers-with-action-mailer,让它工作。
【讨论】:
仅供参考 - 合并!返回一个没有传递方法的哈希。 提到的博客文章的 URL 已更改,现在可以在 dev.scottw.com/multiple-smtp-servers-with-action-mailer 找到。 相关 - ***.com/a/6413630/242426。还有一个合并到 Rails 4+ 中的拉取请求,以添加对此开箱即用的支持 - github.com/rails/rails/pull/7397【参考方案6】:Rails-2.3.*
# app/models/user_mailer.rb
class UserMailer < ActionMailer::Base
def self.smtp_settings
USER_MAILER_SMTP_SETTINGS
end
def spam(user)
recipients user.mail
from 'spam@example.com'
subject 'Enlarge whatever!'
body :user => user
content_type 'text/html'
end
end
# config/environment.rb
ActionMailer::Base.smtp_settings = standard_smtp_settings
USER_MAILER_SMTP_SETTINGS = user_smtp_settings
# From console or whatever...
UserMailer.deliver_spam(user)
【讨论】:
【参考方案7】:恐怕这在本地是不可行的。 但是你可以通过修改模型中的@@smtp_settings 变量来欺骗一下。
有一篇关于 Oreilly 的文章很好地解释了这一点,即使他们的代码根本不干净。 http://broadcast.oreilly.com/2009/03/using-multiple-smtp-accounts-w.html
【讨论】:
感谢您的回复。我看到了那篇文章,这就是我在每封邮件之前重新加载 smtp 设置的意思(这就是文章在 load_settings 方法中所做的)。这接近于一个解决方案,但它弄乱了 ExceptionNotifier,因为我无法在不更改插件的情况下重新加载它的设置。我希望那里有更易于维护的东西。 不幸的是,由于它的实现方式,这是不可能的。 只要有足够的猴子补丁,一切皆有可能。 请帮助将此 PR 合并到 Rails 中。 (github.com/rails/rails/pull/7397)。这完全符合 OP 的想法。【参考方案8】:https://github.com/AnthonyCaliendo/action_mailer_callbacks
我发现这个插件很容易帮我解决了这个问题(如
class CustomMailer < ActionMailer::Base
before_deliver do |mail|
self.smtp_settings = custom_settings
end
after_deliver do |mail|
self.smtp_settings = default_settings
end
def some_message
subject "blah"
recipients "blah@blah.com"
from "ruby.ninja@ninjaness.com"
body "You can haz Ninja rb skillz!"
attachment some_doc
end
end
【讨论】:
【参考方案9】:这是另一种解决方案,虽然看起来很荒谬,但我认为它更简洁,更容易在不同的 AM::Base 类中重用:
module FTTUtilities
module ActionMailer
module ClassMethods
def smtp_settings
dict = YAML.load_file(RAILS_ROOT + "/config/custom_mailers.yml")[self.name.underscore]
@custom_smtp_settings ||= HashWithIndifferentAccess.new(dict)
end
end
module InstanceMethods
def smtp_settings
self.class.smtp_settings
end
end
end
end
邮件程序示例:
class CustomMailer < ActionMailer::Base
extend FTTUtilites::ActionMailer::ClassMethods
include FTTUtilites::ActionMailer::InstanceMethods
end
【讨论】:
【参考方案10】:Rails 3.2 解决方案:
class SomeMailer < ActionMailer::Base
include AbstractController::Callbacks
after_filter :set_delivery_options
private
def set_delivery_options
settings =
:address => 'smtp.server',
:port => 587,
:domain => 'your_domain',
:user_name => 'smtp_username',
:password => 'smtp_password',
:authentication => 'PLAIN' # or something
message.delivery_method.settings.merge!(settings)
end
end
受How to send emails with multiple, dynamic smtp using Actionmailer/Ruby on Rails启发的解决方案
【讨论】:
【参考方案11】:当我想在控制台 (Rails 5) 中进行快速测试时,我执行了以下操作:
settings = username: "" # etc
mailer = MyMailer.some_method
mailer.delivery_method.settings.merge!(settings)
mailer.deliver
【讨论】:
以上是关于带有多个 SMTP 服务器的 Rails ActionMailer的主要内容,如果未能解决你的问题,请参考以下文章