跳至内容 跳至搜索

Action Mailer Base

Action Mailer 允许您使用邮件器模型和视图从应用程序发送电子邮件。

邮件器模型

要使用 Action Mailer,您需要创建一个邮件器模型。

$ bin/rails generate mailer Notifier

生成的模型继承自 ApplicationMailer,而 ApplicationMailer 又继承自 ActionMailer::Base。邮件器模型定义了用于生成电子邮件的方法。在这些方法中,您可以设置将在邮件器视图中使用、邮件本身上的选项(如 :from 地址)以及附件。

class ApplicationMailer < ActionMailer::Base
  default from: 'from@example.com'
  layout 'mailer'
end

class NotifierMailer < ApplicationMailer
  default from: 'no-reply@example.com',
          return_path: 'system@example.com'

  def welcome(recipient)
    @account = recipient
    mail(to: recipient.email_address_with_name,
         bcc: ["bcc@example.com", "Order Watcher <watcher@example.com>"])
  end
end

在邮件器方法中,您可以访问以下方法:

  • attachments[]= - 允许您以直观的方式向电子邮件添加附件;attachments['filename.png'] = File.read('path/to/filename.png')

  • attachments.inline[]= - 允许您以与 attachments[]= 相同的方式向电子邮件添加内联附件。

  • headers[]= - 允许您在电子邮件中指定任何标题字段,例如 headers['X-No-Spam'] = 'True'。请注意,多次声明一个标题会添加许多同名的字段。有关更多信息,请阅读 headers 文档。

  • headers(hash) - 允许您在电子邮件中指定多个标题,例如 headers({'X-No-Spam' => 'True', 'In-Reply-To' => '1234@message.id'})

  • mail - 允许您指定要发送的电子邮件。

传递给 mail 方法的哈希允许您指定 Mail::Message 可以接受的任何标题(任何有效的电子邮件标题,包括可选字段)。

如果 mail 方法未传递块,它将检查您的视图,并发送与该方法同名的所有视图,因此上述操作将以 multipart/alternative 电子邮件的形式发送 welcome.text.erb 视图文件以及 welcome.html.erb 视图文件。

如果您想显式渲染特定模板,请传递一个块。

mail(to: user.email) do |format|
  format.text
  format.html
end

块语法在提供特定于某一部分的信息方面也很有用。

mail(to: user.email) do |format|
  format.text(content_transfer_encoding: "base64")
  format.html
end

甚至可以渲染一个特殊视图。

mail(to: user.email) do |format|
  format.text
  format.html { render "some_other_template" }
end

邮件器视图

与 Action Controller 类似,每个邮件器类都有一个对应的视图目录,其中类的每个方法都在其中查找与其名称匹配的模板。

要为邮件器定义一个模板,请在您的邮件器模型中创建一个与方法同名的 .erb 文件。例如,在上面定义的邮件器中,app/views/notifier_mailer/welcome.text.erb 的模板将用于生成电子邮件。

在邮件器方法中定义的变量可以作为实例变量在其对应的视图中访问。

默认情况下,电子邮件以纯文本发送,因此我们模型示例的示例文本视图可能如下所示:

Hi <%= @account.name %>,
Thanks for joining our service! Please check back often.

您甚至可以在这些视图中使用 Action View 助手。例如:

You got a new note!
<%= truncate(@note.body, length: 25) %>

如果您需要访问视图中的主题、发件人或收件人,您可以通过邮件对象进行访问。

You got a new note from <%= message.from %>!
<%= truncate(@note.body, length: 25) %>

生成 URL

可以使用 url_for 或命名路由在邮件器视图中生成 URL。与 Action Pack 中的控制器不同,邮件器实例没有任何关于传入请求的上下文,因此您需要提供生成 URL 所需的所有详细信息。

使用 url_for 时,您需要提供 :host:controller:action

<%= url_for(host: "example.com", controller: "welcome", action: "greeting") %>

使用命名路由时,您只需要提供 :host

<%= users_url(host: "example.com") %>

您应该使用 named_route_url 样式(生成绝对 URL)并避免使用 named_route_path 样式(生成相对 URL),因为阅读邮件的客户端将无法理解当前 URL 以确定相对路径。

还可以通过在 config/application.rb 中设置 :host 选项作为配置选项,来为所有邮件器设置一个默认主机。

config.action_mailer.default_url_options = { host: "example.com" }

您也可以在单个邮件器上定义 default_url_options 方法,以按邮件器覆盖这些默认设置。

默认情况下,当 config.force_ssltrue 时,为主机生成的 URL 将使用 HTTPS 协议。

发送邮件

一旦定义了邮件器操作和模板,您就可以立即发送消息,或者将其创建和发送推迟到以后。

NotifierMailer.welcome(User.first).deliver_now # sends the email
mail = NotifierMailer.welcome(User.first)      # => an ActionMailer::MessageDelivery object
mail.deliver_now                               # generates and sends the email now

ActionMailer::MessageDelivery 类是一个包装器,它会调用您的方法来生成邮件。如果您想直接访问委托人或 Mail::Message,您可以调用 ActionMailer::MessageDelivery 对象上的 message 方法。

NotifierMailer.welcome(User.first).message     # => a Mail::Message object

Action Mailer 与 Active Job 良好集成,因此您可以在后台生成和发送电子邮件(例如:在请求-响应周期之外,这样用户就无需等待)。

NotifierMailer.welcome(User.first).deliver_later # enqueue the email sending to Active Job

请注意,deliver_later 将在后台作业中执行您的方法。

您永远不会实例化您的邮件器类。相反,您只需在类本身上调用您定义的方法。所有实例方法都应返回要发送的邮件对象。

多部分邮件

也可以隐式使用多部分消息,因为 Action Mailer 将自动检测并使用多部分模板,其中每个模板都以操作名称命名,后跟内容类型。每个检测到的模板都将作为单独的部分添加到消息中。

例如,如果存在以下模板:

  • signup_notification.text.erb

  • signup_notification.html.erb

  • signup_notification.xml.builder

  • signup_notification.yml.erb

每个都将被渲染并作为单独的部分添加到消息中,具有相应的内容类型。整个消息的内容类型将自动设置为 multipart/alternative,这表示电子邮件包含同一电子邮件正文的多个不同表示形式。在操作中定义的相同实例变量将传递给所有电子邮件模板。

如果已向电子邮件添加任何附件或部分,则不会执行隐式模板渲染。这意味着您必须手动将每个部分添加到电子邮件中,并将电子邮件的内容类型设置为 multipart/alternative

附件

在电子邮件中发送附件很容易。

class NotifierMailer < ApplicationMailer
  def welcome(recipient)
    attachments['free_book.pdf'] = File.read('path/to/file.pdf')
    mail(to: recipient, subject: "New account information")
  end
end

如果(在视图目录中)同时存在 welcome.text.erbwelcome.html.erb 模板,它将发送一个完整的 multipart/mixed 电子邮件,其中包含两个部分:第一部分是包含文本和 HTML 电子邮件部分的 multipart/alternative;第二部分是 Base64 编码的 file.pdf 副本,文件名为 free_book.pdf

如果您需要发送没有内容的附件,您需要为其创建一个空的视图,或者像这样添加一个空的 body 参数:

class NotifierMailer < ApplicationMailer
  def welcome(recipient)
    attachments['free_book.pdf'] = File.read('path/to/file.pdf')
    mail(to: recipient, subject: "New account information", body: "")
  end
end

您还可以发送带有 HTML 模板的附件,在这种情况下,您需要像这样添加 body、附件和自定义内容类型:

class NotifierMailer < ApplicationMailer
  def welcome(recipient)
    attachments["free_book.pdf"] = File.read("path/to/file.pdf")
    mail(to: recipient,
         subject: "New account information",
         content_type: "text/html",
         body: "<html><body>Hello there</body></html>")
  end
end

内联附件

您还可以指定一个文件应与 HTML 一起内联显示。如果您想显示公司徽标或照片,这将很有用。

class NotifierMailer < ApplicationMailer
  def welcome(recipient)
    attachments.inline['photo.png'] = File.read('path/to/photo.png')
    mail(to: recipient, subject: "Here is what we look like")
  end
end

然后,要在视图中引用该图像,请创建一个 welcome.html.erb 文件,并调用 image_tag,将您想显示的附件作为参数传递,然后调用附件的 url 来获取图像源的相对内容 ID 路径。

<h1>Please Don't Cringe</h1>

<%= image_tag attachments['photo.png'].url -%>

由于我们使用了 Action View 的 image_tag 方法,您可以传递任何其他所需的选项。

<h1>Please Don't Cringe</h1>

<%= image_tag attachments['photo.png'].url, alt: 'Our Photo', class: 'photo' -%>

观察和拦截邮件

Action Mailer 提供了对 Mail 观察者和拦截器方法的钩子。这些允许您注册在邮件发送生命周期中调用的类。

观察者类必须实现 :delivered_email(message) 方法,该方法将在每封发送的电子邮件发送后调用一次。

拦截器类必须实现 :delivering_email(message) 方法,该方法将在电子邮件发送之前调用,允许您在电子邮件被发送到邮件代理之前对其进行修改。您的类应该直接对传入的 Mail::Message 实例进行任何所需的修改。

默认哈希

Action Mailer 为您的电子邮件提供了一些智能默认设置,这些设置通常在类定义内的 default 方法中指定。

class NotifierMailer < ApplicationMailer
  default sender: 'system@example.com'
end

您可以传递 Mail::Message 接受的任何标题值。开箱即用,ActionMailer::Base 设置了以下内容:

  • mime_version: "1.0"

  • charset: "UTF-8"

  • content_type: "text/plain"

  • parts_order: [ "text/plain", "text/enriched", "text/html" ]

parts_ordercharset 实际上不是有效的 Mail::Message 标题字段,但 Action Mailer 会适当地翻译它们并设置正确的值。

由于您可以传递任何标题,因此您需要将标题加引号作为字符串,或将其作为下划线符号传递,这样以下内容才能正常工作:

class NotifierMailer < ApplicationMailer
  default 'Content-Transfer-Encoding' => '7bit',
          content_description: 'This is a description'
end

最后,Action Mailer 还支持将 ProcLambda 对象传递到默认哈希中,因此您可以定义在生成消息时进行评估的方法。

class NotifierMailer < ApplicationMailer
  default 'X-Special-Header' => Proc.new { my_method }, to: -> { @inviter.email_address }

  private
    def my_method
      'some complex call'
    end
end

请注意,proc/lambda 会在邮件消息生成开始时进行评估,因此如果您使用 proc 在默认哈希中设置了某项内容,然后又在邮件器方法中设置了相同的内容,它将被邮件器方法覆盖。

也可以通过 config/application.rb 中的 default_options= 配置来设置将在所有邮件器中使用的这些默认选项。

config.action_mailer.default_options = { from: "no-reply@example.org" }

回调

您可以使用 before_actionafter_action 来指定回调,以配置您的消息,并使用 before_deliverafter_deliver 来包装发送过程。例如,当您想为某个邮件器类发送的所有消息添加默认的内联附件并记录发送情况时:

class NotifierMailer < ApplicationMailer
  before_action :add_inline_attachment!
  after_deliver :log_delivery

  def welcome
    mail
  end

  private
    def add_inline_attachment!
      attachments.inline["footer.jpg"] = File.read('/path/to/filename.jpg')
    end

    def log_delivery
      Rails.logger.info "Sent email with message id '#{message.message_id}' at #{Time.current}."
    end
end

Action Mailer 中的 Action 回调是通过 AbstractController::Callbacks 实现的,因此您可以使用与继承自 ActionController::Base 的类中回调相同的方式来定义和配置回调。

请注意,除非您有特殊原因,否则在 Action Mailer 类中应优先使用 before_action 而不是 after_action,以确保正确解析标题。

捕获错误

邮件器方法内的 rescue 块无法捕获渲染之外发生的错误,例如后台作业中的记录反序列化错误,或第三方邮件递送服务中的错误。

要捕获在邮件发送过程的任何部分发生的错误,请使用 rescue_from

class NotifierMailer < ApplicationMailer
  rescue_from ActiveJob::DeserializationError do
    # ...
  end

  rescue_from "SomeThirdPartyService::ApiError" do
    # ...
  end

  def notify(recipient)
    mail(to: recipient, subject: "Notification")
  end
end

预览电子邮件

您可以通过将邮件器预览文件添加到 ActionMailer::Base.preview_paths 来可视化地预览您的电子邮件模板。由于大多数电子邮件都涉及数据库数据,因此您需要编写一些场景来加载带有假数据的消息。

class NotifierMailerPreview < ActionMailer::Preview
  def welcome
    NotifierMailer.welcome(User.first)
  end
end

方法必须返回一个 Mail::Message 对象,可以通过调用邮件器方法而不带额外的 deliver_now / deliver_later 来生成该对象。邮件预览目录的位置可以通过 preview_paths 选项进行配置,该选项的默认值为 test/mailers/previews

config.action_mailer.preview_paths << "#{Rails.root}/lib/mailer_previews"

在运行的开发服务器实例上,可以通过 https://:3000/rails/mailers 访问所有预览的概述。

可以像拦截邮件一样拦截 Previews,方法是注册一个具有 previewing_email 方法的预览拦截器。

class CssInlineStyler
  def self.previewing_email(message)
    # inline CSS styles
  end
end

config.action_mailer.preview_interceptors :css_inline_styler

请注意,如果拦截器应该同时作用于发送和预览电子邮件,则需要同时使用 register_interceptorregister_preview_interceptor 注册它们。

配置选项

这些选项在类级别指定,例如 ActionMailer::Base.raise_delivery_errors = true

  • default_options - 您可以在类级别以及类本身中(如上文所述)传递此项。

  • logger - 如果可用,日志记录器用于生成有关邮件运行的信息。可以设置为 nil 以禁用日志记录。与 Ruby 自己的 Logger 和 Log4r 日志记录器兼容。

  • smtp_settings - 允许详细配置 :smtp 递送方法。

    • :address - 允许您使用远程邮件服务器。只需将其从默认的“localhost”设置更改即可。

    • :port - 如果您的邮件服务器不运行在端口 25 上,您可以更改它。

    • :domain - 如果您需要指定 HELO 域,可以在此处进行。

    • :user_name - 如果您的邮件服务器需要身份验证,请在此设置中设置用户名。

    • :password - 如果您的邮件服务器需要身份验证,请在此设置中设置密码。

    • :authentication - 如果您的邮件服务器需要身份验证,您需要在此指定身份验证类型。这是一个符号,可以是 :plain(将发送 Base64 编码的密码)、:login(将发送 Base64 编码的密码)或 :cram_md5(结合了挑战/响应机制来交换信息和加密的消息 Digest 5 算法来哈希重要信息)。

    • :enable_starttls - 连接到 SMTP 服务器时使用 STARTTLS,如果不支持则失败。默认为 false。需要 Mail gem 的至少 2.7 版本。

    • :enable_starttls_auto - 检测您的 SMTP 服务器是否启用了 STARTTLS 并开始使用它。默认为 true

    • :openssl_verify_mode - 使用 TLS 时,您可以设置 OpenSSL 如何检查证书。如果您需要验证自签名和/或通配符证书,这将非常有用。您可以使用 OpenSSL 验证常量的名称('none''peer')或直接使用常量(OpenSSL::SSL::VERIFY_NONEOpenSSL::SSL::VERIFY_PEER)。

    • :ssl/:tls - 使 SMTP 连接能够使用 SMTP/TLS(SMTPS:通过直接 TLS 连接的 SMTP)。

    • :open_timeout - 尝试打开连接时等待的秒数。

    • :read_timeout - 等待读取(2)调用超时前的秒数。

  • sendmail_settings - 允许您覆盖 :sendmail 递送方法的选项。

    • :location - sendmail 可执行文件的位置。默认为 /usr/sbin/sendmail

    • :arguments - 命令行参数。默认为 %w[ -i ],在发送消息之前会自动添加 -f sender@address

  • file_settings - 允许您覆盖 :file 递送方法的选项。

    • :location - 将写入电子邮件的目录。默认为应用程序的 tmp/mails

  • raise_delivery_errors - 如果电子邮件未能成功发送,是否应引发错误。

  • delivery_method - 定义递送方法。可能的值是 :smtp(默认)、:sendmail:test:file。或者,您可以提供一个自定义递送方法对象,例如 MyOwnDeliveryMethodClass。请参阅 Mail gem 文档,了解自定义递送代理需要实现哪些接口。

  • perform_deliveries - 确定调用邮件消息上的 .deliver 或 Action Mailer 方法时,Action Mailer 是否实际发送电子邮件。此选项默认开启,但可以关闭以辅助功能测试。

  • deliveries - 保留通过 Action Mailer 和 delivery_method :test 发送的所有电子邮件的数组。最常用于单元和功能测试。

  • delivery_job - 与 deliver_later 一起使用的作业类。邮件器可以将此设置为使用自定义递送作业。默认为 ActionMailer::MailDeliveryJob

  • deliver_later_queue_name - 默认 delivery_jobdeliver_later 使用的队列名称。邮件器可以将其设置为使用自定义队列名称。

命名空间
方法
A
C
D
E
H
M
N
R
S
U
包含的模块

常量

PROTECTED_IVARS = AbstractController::Rendering::DEFAULT_PROTECTED_INSTANCE_VARIABLES + [:@_action_has_layout]
 

Attributes

[W] mailer_name

允许设置当前邮件器的名称。

类公共方法

controller_path()

别名: mailer_name

default(value = nil)

允许通过应用程序配置设置默认值。

config.action_mailer.default_options = { from: "no-reply@example.org" }
也别名为: default_options=
# File actionmailer/lib/action_mailer/base.rb, line 581
def default(value = nil)
  self.default_params = default_params.merge(value).freeze if value
  default_params
end

default_options=(value = nil)

别名: default

email_address_with_name(address, name)

以“Name <email@example.com>”格式返回电子邮件。

如果名称是空字符串,它将只返回地址。

# File actionmailer/lib/action_mailer/base.rb, line 603
def email_address_with_name(address, name)
  Mail::Address.new.tap do |builder|
    builder.address = address
    builder.display_name = name.presence
  end.to_s
end

mailer_name()

返回当前邮件器的名称。此方法也用作查找视图的路径。如果这是匿名邮件器,此方法将返回 anonymous

也别名为: controller_path
# File actionmailer/lib/action_mailer/base.rb, line 571
def mailer_name
  @mailer_name ||= anonymous? ? "anonymous" : name.underscore
end

new()

# File actionmailer/lib/action_mailer/base.rb, line 639
def initialize
  super()
  @_mail_was_called = false
  @_message = Mail.new
end

register_interceptor(interceptor)

注册一个将在邮件发送前调用的拦截器。可以传递类、字符串或符号作为拦截器。如果传递字符串或符号,它将被驼峰化并实例化。

# File actionmailer/lib/action_mailer/base.rb, line 548
def register_interceptor(interceptor)
  Mail.register_interceptor(observer_class_for(interceptor))
end

register_interceptors(*interceptors)

注册一个或多个将在邮件发送前调用的拦截器。

# File actionmailer/lib/action_mailer/base.rb, line 522
def register_interceptors(*interceptors)
  interceptors.flatten.compact.each { |interceptor| register_interceptor(interceptor) }
end

register_observer(observer)

注册一个将在邮件发送时通知的观察者。可以传递类、字符串或符号作为观察者。如果传递字符串或符号,它将被驼峰化并实例化。

# File actionmailer/lib/action_mailer/base.rb, line 534
def register_observer(observer)
  Mail.register_observer(observer_class_for(observer))
end

register_observers(*observers)

注册一个或多个将在邮件发送时通知的观察者。

# File actionmailer/lib/action_mailer/base.rb, line 512
def register_observers(*observers)
  observers.flatten.compact.each { |observer| register_observer(observer) }
end

supports_path?()

电子邮件不支持相对路径链接。

# File actionmailer/lib/action_mailer/base.rb, line 938
def self.supports_path? # :doc:
  false
end

unregister_interceptor(interceptor)

取消注册先前注册的拦截器。可以传递类、字符串或符号作为拦截器。如果传递字符串或符号,它将被驼峰化并实例化。

# File actionmailer/lib/action_mailer/base.rb, line 555
def unregister_interceptor(interceptor)
  Mail.unregister_interceptor(observer_class_for(interceptor))
end

unregister_interceptors(*interceptors)

取消注册一个或多个先前注册的拦截器。

# File actionmailer/lib/action_mailer/base.rb, line 527
def unregister_interceptors(*interceptors)
  interceptors.flatten.compact.each { |interceptor| unregister_interceptor(interceptor) }
end

unregister_observer(observer)

取消注册先前注册的观察者。可以传递类、字符串或符号作为观察者。如果传递字符串或符号,它将被驼峰化并实例化。

# File actionmailer/lib/action_mailer/base.rb, line 541
def unregister_observer(observer)
  Mail.unregister_observer(observer_class_for(observer))
end

unregister_observers(*observers)

取消注册一个或多个先前注册的观察者。

# File actionmailer/lib/action_mailer/base.rb, line 517
def unregister_observers(*observers)
  observers.flatten.compact.each { |observer| unregister_observer(observer) }
end

实例公共方法

attachments()

允许您像这样向电子邮件添加附件:

mail.attachments['filename.jpg'] = File.read('/path/to/filename.jpg')

如果您这样做,那么 Mail 将会解析文件名并得出 MIME 类型。它还将设置 Content-TypeContent-DispositionContent-Transfer-Encoding,并将附件的内容编码为 Base64。

如果您想指定覆盖,可以传递一个哈希而不是字符串。

mail.attachments['filename.jpg'] = {mime_type: 'application/gzip',
                                    content: File.read('/path/to/filename.jpg')}

如果您想使用 Base64 以外的其他编码,您需要提供编码类型以及预编码的内容,因为 Mail 不知道如何解码数据。

file_content = SpecialEncode(File.read('/path/to/filename.jpg'))
mail.attachments['filename.jpg'] = {mime_type: 'application/gzip',
                                    encoding: 'SpecialEncoding',
                                    content: file_content }

您还可以搜索特定的附件。

# By Filename
mail.attachments['filename.jpg']   # => Mail::Part object or nil

# or by index
mail.attachments[0]                # => Mail::Part (first attachment)
# File actionmailer/lib/action_mailer/base.rb, line 756
def attachments
  if @_mail_was_called
    LateAttachmentsProxy.new(@_message.attachments)
  else
    @_message.attachments
  end
end

email_address_with_name(address, name)

以“Name <email@example.com>”格式返回电子邮件。

如果名称是空字符串,它将只返回地址。

# File actionmailer/lib/action_mailer/base.rb, line 680
def email_address_with_name(address, name)
  self.class.email_address_with_name(address, name)
end

headers(args = nil)

允许您将随机和不寻常的标题传递给新的 Mail::Message 对象,该对象会将它们添加到自身。

headers['X-Special-Domain-Specific-Header'] = "SecretValue"

您也可以将一个包含标题字段名称和值的哈希传递到 headers 中,这些将被设置在 Mail::Message 对象上。

headers 'X-Special-Domain-Specific-Header' => "SecretValue",
        'In-Reply-To' => incoming.message_id

生成的 Mail::Message 将在其标题中包含以下内容:

X-Special-Domain-Specific-Header: SecretValue

关于替换已定义标题的注意事项

  • subject

  • sender

  • from

  • cc

  • bcc

  • reply-to

  • orig-date

  • message-id

  • references

字段在电子邮件标题中只能出现一次,而其他字段(如 X-Anything)可以出现多次。

如果您想替换任何已存在的标题,请先将其设置为 nil 以重置值,否则将为同一个标题添加另一个字段。

# File actionmailer/lib/action_mailer/base.rb, line 718
def headers(args = nil)
  if args
    @_message.headers(args)
  else
    @_message
  end
end

mail(headers = {}, &block)

创建消息和渲染电子邮件模板的主要方法。有两种调用此方法的方式:带块调用和不带块调用。

它接受一个标题哈希。此哈希允许您指定电子邮件消息中最常用的标题,这些标题是:

  • :subject - 消息的主题。如果省略,Action Mailer 将在 [mailer_scope, action_name] 的作用域中为 Rails I18n 类请求翻译的 :subject,如果不存在,则将翻译 action_name 的人性化版本。

  • :to - 消息的收件人,可以是地址字符串,也可以是地址数组。

  • :from - 消息的发件人。

  • :cc - 您想抄送(Carbon-Copy)此电子邮件的收件人,可以是地址字符串,也可以是地址数组。

  • :bcc - 您想秘密抄送(Blind-Carbon-Copy)此电子邮件的收件人,可以是地址字符串,也可以是地址数组。

  • :reply_to - 设置电子邮件 Reply-To 标题的收件人。

  • :date - 电子邮件发送的日期。

您可以使用 ::default 类方法为上述任何标题(:date 除外)设置默认值。

class Notifier < ActionMailer::Base
  default from: 'no-reply@test.lindsaar.net',
          bcc: 'email_logger@test.lindsaar.net',
          reply_to: 'bounces@test.lindsaar.net'
end

如果您需要上面未列出的其他标题,您可以将它们作为标题哈希的一部分传递,或者使用 headers['name'] = value 方法。

:return_path 指定为标题时,该值将用作 Mail 消息的“信封发件人”地址。设置此项在您希望将送达通知发送到与 :from 地址不同的地址时很有用。 Mail 实际上将优先使用 :return_path,然后是 :sender,最后是 :from 字段作为“信封发件人”值。

如果您不向 mail 方法传递块,它将在视图路径中查找所有模板,默认情况下使用邮件器名称和调用它的方法名。然后,它将智能地为每个模板创建部分,并根据正确的 Mime 类型和顺序做出合理猜测,并返回一个完全准备好的 Mail::Message 对象,可以调用 :deliver 来发送。

例如

class Notifier < ActionMailer::Base
  default from: 'no-reply@test.lindsaar.net'

  def welcome
    mail(to: 'mikel@test.lindsaar.net')
  end
end

将查找“app/views/notifier”下名为“welcome”的所有模板。如果不存在 welcome 模板,则会引发 ActionView::MissingTemplate 错误。

但是,这些可以自定义。

mail(template_path: 'notifications', template_name: 'another')

现在它将查找“app/views/notifications”下名为“another”的所有模板。

如果您传递一个块,您可以渲染您选择的特定模板。

mail(to: 'mikel@test.lindsaar.net') do |format|
  format.text
  format.html
end

您甚至可以直接渲染纯文本而不使用模板。

mail(to: 'mikel@test.lindsaar.net') do |format|
  format.text { render plain: "Hello Mikel!" }
  format.html { render html: "<h1>Hello Mikel!</h1>".html_safe }
end

这将渲染一个包含 text/plaintext/html 部分的 multipart/alternative 电子邮件。

块语法还允许您在需要时自定义部分标题。

mail(to: 'mikel@test.lindsaar.net') do |format|
  format.text(content_transfer_encoding: "base64")
  format.html
end
# File actionmailer/lib/action_mailer/base.rb, line 865
def mail(headers = {}, &block)
  return message if @_mail_was_called && headers.blank? && !block

  # At the beginning, do not consider class default for content_type
  content_type = headers[:content_type]

  headers = apply_defaults(headers)

  # Apply charset at the beginning so all fields are properly quoted
  message.charset = charset = headers[:charset]

  # Set configure delivery behavior
  wrap_delivery_behavior!(headers[:delivery_method], headers[:delivery_method_options])

  assign_headers_to_message(message, headers)

  # Render the templates and blocks
  responses = collect_responses(headers, &block)
  @_mail_was_called = true

  create_parts_from_responses(message, responses)
  wrap_inline_attachments(message)

  # Set up content type, reapply charset and handle parts order
  message.content_type = set_content_type(message, content_type, headers[:content_type])
  message.charset      = charset

  if message.multipart?
    message.body.set_sort_order(headers[:parts_order])
    message.body.sort_parts!
  end

  message
end

mailer_name()

返回邮件器对象的名称。

# File actionmailer/lib/action_mailer/base.rb, line 673
def mailer_name
  self.class.mailer_name
end

实例私有方法

default_i18n_subject(interpolations = {})

使用 Rails I18n 类在 [mailer_scope, action_name] 作用域下翻译 subject。如果它在指定的作用域下找不到 subject 的翻译,它将默认为 action_name 的人性化版本。如果主题包含插值,您可以将其通过 interpolations 参数传递。

# File actionmailer/lib/action_mailer/base.rb, line 932
def default_i18n_subject(interpolations = {}) # :doc:
  mailer_scope = self.class.mailer_name.tr("/", ".")
  I18n.t(:subject, **interpolations, scope: [mailer_scope, action_name], default: action_name.humanize)
end

set_content_type(m, user_content_type, class_default)

mail 使用来设置邮件的内容类型。

它将使用给定的 user_content_type,或者在邮件消息有任何附件时使用 multipart。如果附件是内联的,内容类型将是“multipart/related”,否则是“multipart/mixed”。

如果没有通过标题传递内容类型,并且没有附件,或者消息是多部分的,那么将使用默认内容类型。

# File actionmailer/lib/action_mailer/base.rb, line 910
def set_content_type(m, user_content_type, class_default) # :doc:
  params = m.content_type_parameters || {}
  case
  when user_content_type.present?
    user_content_type
  when m.has_attachments?
    if m.attachments.all?(&:inline?)
      ["multipart", "related", params]
    else
      ["multipart", "mixed", params]
    end
  when m.multipart?
    ["multipart", "alternative", params]
  else
    m.content_type || class_default
  end
end