更多内容在 rubyonrails.org:

1 什么是 Action Mailer?

Action Mailer 允许您从 Rails 应用程序发送电子邮件。它是 Rails 框架中两个与电子邮件相关的组件之一。另一个是 Action Mailbox,它处理接收电子邮件。

Action Mailer 使用类(称为“邮件器”)和视图来创建和配置要发送的电子邮件。邮件器是继承自 ActionMailer::Base 的类。邮件器类类似于控制器类。两者都有

  • 在视图中可访问的实例变量。
  • 使用布局和部分的能力。
  • 访问 params 哈希的能力。
  • app/views 中的操作和相关视图。

2 创建邮件器和视图

本节将提供使用 Action Mailer 发送电子邮件的分步指南。以下是每个步骤的详细信息。

2.1 生成邮件器

首先,您使用“邮件器”生成器来创建与邮件器相关的类

$ bin/rails generate mailer User
create  app/mailers/user_mailer.rb
invoke  erb
create    app/views/user_mailer
invoke  test_unit
create    test/mailers/user_mailer_test.rb
create    test/mailers/previews/user_mailer_preview.rb

与下面的 UserMailer 一样,所有生成的邮件器类都继承自 ApplicationMailer

# app/mailers/user_mailer.rb
class UserMailer < ApplicationMailer
end

ApplicationMailer 类继承自 ActionMailer::Base,可用于定义所有邮件器共有的属性

# app/mailers/application_mailer.rb
class ApplicationMailer < ActionMailer::Base
  default from: "[email protected]"
  layout "mailer"
end

如果您不想使用生成器,也可以手动将文件添加到 app/mailers 目录。确保您的类继承自 ApplicationMailer

# app/mailers/custom_mailer.rb
class CustomMailer < ApplicationMailer
end

2.2 编辑邮件器

app/mailers/user_mailer.rb 中的 UserMailer 最初没有任何方法。因此,接下来,我们向邮件器添加方法(也称为操作),这些方法将发送特定的电子邮件。

邮件器具有称为“操作”的方法,它们使用视图来构建其内容,类似于控制器。控制器生成 HTML 内容以发送回客户端,而邮件器创建要通过电子邮件传递的消息。

让我们向 UserMailer 添加一个名为 welcome_email 的方法,该方法将向用户的注册电子邮件地址发送电子邮件

class UserMailer < ApplicationMailer
  default from: "[email protected]"

  def welcome_email
    @user = params[:user]
    @url  = "http://example.com/login"
    mail(to: @user.email, subject: "Welcome to My Awesome Site")
  end
end

邮件器中的方法名称不必以 _email 结尾。

以下是上述邮件器相关方法的简要说明

  • default 方法为从邮件器发送的所有电子邮件设置默认值。在本例中,我们使用它来为此类中的所有消息设置 :from 标头值。这可以在每个电子邮件的基础上被覆盖。
  • mail 方法创建实际的电子邮件消息。我们使用它来为每个电子邮件指定 :to:subject 等标头的值。

还有一个 headers 方法(上面未使用),它用于使用哈希或通过调用 headers[:field_name] = 'value' 来指定电子邮件标头。

可以在使用生成器时直接指定操作,例如

$ bin/rails generate mailer User welcome_email

以上将生成 UserMailer,其中包含一个空 welcome_email 方法。

您还可以从单个邮件器类发送多封电子邮件。将相关的电子邮件分组在一起可能很方便。例如,上面的 UserMailer 可以包含 goodbye_email(以及相应的视图)以及 welcome_email

2.3 创建邮件器视图

接下来,对于 welcome_email 操作,您需要在 app/views/user_mailer/ 目录中创建一个名为 welcome_email.html.erb 的匹配视图。这是一个可用于欢迎电子邮件的示例 HTML 模板

<h1>Welcome to example.com, <%= @user.name %></h1>
<p>
  You have successfully signed up to example.com,
  your username is: <%= @user.login %>.<br>
</p>
<p>
  To login to the site, just follow this link: <%= link_to 'login`, login_url %>.
</p>
<p>Thanks for joining and have a great day!</p>

以上是 <body> 标签的内容。它将被嵌入默认邮件器布局中,该布局包含 <html> 标签。有关更多信息,请参见 邮件器布局

您还可以创建上述电子邮件的文本版本,并将其存储在 app/views/user_mailer/ 目录中的 welcome_email.text.erb 中(注意 .text.erb 扩展名与 html.erb 扩展名不同)。发送两种格式被认为是最佳实践,因为在 HTML 渲染出现问题的情况下,文本版本可以作为可靠的后备。这是一个示例文本电子邮件

Welcome to example.com, <%= @user.name %>
===============================================

You have successfully signed up to example.com,
your username is: <%= @user.login %>.

To login to the site, just follow this link: <%= @url %>.

Thanks for joining and have a great day!

注意,在 HTML 和文本电子邮件模板中,您可以使用实例变量 @user@url

现在,当您调用 mail 方法时,Action Mailer 将检测到这两个模板(文本和 HTML),并自动生成一个 multipart/alternative 电子邮件。

2.4 调用邮件器

设置好邮件器类和视图后,下一步是实际调用邮件器方法来渲染电子邮件视图(即发送电子邮件)。邮件器可以被认为是渲染视图的另一种方式。控制器操作渲染一个视图,该视图通过 HTTP 协议发送。邮件器操作渲染一个视图,并通过电子邮件协议发送。

让我们看一个使用 UserMailer 在成功创建用户时发送欢迎电子邮件的示例。

首先,让我们创建一个 User 脚手架

$ bin/rails generate scaffold user name email login
$ bin/rails db:migrate

接下来,我们编辑 UserController 中的 create 操作,以便在创建新用户时发送欢迎电子邮件。我们通过在用户成功保存后立即插入对 UserMailer.with(user: @user).welcome_email 的调用来实现这一点。

我们使用 deliver_later 将电子邮件排队以供稍后发送。这样,控制器操作将继续执行,而不会等待电子邮件发送代码运行。deliver_later 方法由 Active Job 支持。

class UsersController < ApplicationController
  # ...

  def create
    @user = User.new(user_params)

    respond_to do |format|
      if @user.save
        # Tell the UserMailer to send a welcome email after save
        UserMailer.with(user: @user).welcome_email.deliver_later

        format.html { redirect_to user_url(@user), notice: "User was successfully created." }
        format.json { render :show, status: :created, location: @user }
      else
        format.html { render :new, status: :unprocessable_entity }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end

  # ...
end

传递给 with 的任何键值对都将成为邮件器操作的 params。例如,with(user: @user, account: @user.account) 使 params[:user]params[:account] 在邮件器操作中可用。

通过上述邮件器、视图和控制器设置,如果您创建了一个新的 User,您可以检查日志以查看是否已发送欢迎电子邮件。日志文件将显示已发送的文本和 HTML 版本,如下所示

[ActiveJob] [ActionMailer::MailDeliveryJob] [ec4b3786-b9fc-4b5e-8153-9153095e1cbf] Delivered mail [email protected] (19.9ms)
[ActiveJob] [ActionMailer::MailDeliveryJob] [ec4b3786-b9fc-4b5e-8153-9153095e1cbf] Date: Thu, 06 Jun 2024 12:43:44 -0500
From: [email protected]
To: [email protected]
Message-ID: <[email protected]>
Subject: Welcome to My Awesome Site
Mime-Version: 1.0
Content-Type: multipart/alternative;
 boundary="--==_mimepart_6661f55086194_1380c7eb869259";
 charset=UTF-8
Content-Transfer-Encoding: 7bit


----==_mimepart_6661f55086194_1380c7eb869259
Content-Type: text/plain;

...

----==_mimepart_6661f55086194_1380c7eb869259
Content-Type: text/html;

...

您也可以从 Rails 控制台中调用邮件器并发送电子邮件,这可能在您设置控制器操作之前很有用。以下将发送与上面相同的 welcome_email

irb> user = User.first
irb> UserMailer.with(user: user).welcome_email.deliver_later

如果您想立即发送电子邮件(例如,从 cron 作业发送),您可以调用 deliver_now

class SendWeeklySummary
  def run
    User.find_each do |user|
      UserMailer.with(user: user).weekly_summary.deliver_now
    end
  end
end

类似于 UserMailer 中的 weekly_summary 方法,该方法将返回一个 ActionMailer::MessageDelivery 对象,该对象具有 deliver_nowdeliver_later 方法,用于立即或稍后发送邮件。ActionMailer::MessageDelivery 对象是 Mail::Message 的包装器。如果您想检查、更改或对 Mail::Message 对象执行其他操作,可以使用 ActionMailer::MessageDelivery 对象的 message 方法访问它。

以下是如何在 Rails 控制台中使用 MessageDelivery 对象的示例

irb> UserMailer.with(user: user).weekly_summary
#<ActionMailer::MailDeliveryJob:0x00007f84cb0367c0
 @_halted_callback_hook_called=nil,
 @_scheduled_at_time=nil,
 @arguments=
  ["UserMailer",
   "welcome_email",
   "deliver_now",
   {:params=>
     {:user=>
       #<User:0x00007f84c9327198
        id: 1,
        name: "Bhumi",
        email: "[email protected]",
        login: "Bhumi",
        created_at: Thu, 06 Jun 2024 17:43:44.424064000 UTC +00:00,
        updated_at: Thu, 06 Jun 2024 17:43:44.424064000 UTC +00:00>},
    :args=>[]}],
 @exception_executions={},
 @executions=0,
 @job_id="07747748-59cc-4e88-812a-0d677040cd5a",
 @priority=nil,

3 多部分邮件和附件

multipart MIME 类型表示由多个组成部分组成的文档,每个组成部分都可以有其自己的 MIME 类型(例如 text/htmltext/plain)。multipart 类型封装了将多个文件一起发送在一个事务中,例如将多个文件附加到电子邮件。

3.1 添加附件

您可以通过将文件名和内容传递给 attachments 方法 来添加附件。Action Mailer 将自动猜测 mime_type、设置 encoding 并创建附件。

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

当触发 mail 方法时,它将发送一封包含附件的多部分邮件,并以正确的方式嵌套,顶层为 multipart/mixed,第一个部分为 multipart/alternative,包含纯文本和 HTML 邮件消息。

另一种发送附件的方法是指定文件名、MIME 类型和编码头以及内容。Action Mailer 将使用您传入的设置。

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

Action Mailer 将自动对附件进行 Base64 编码。如果您想要其他编码方式,可以对内容进行编码,并将编码后的内容以及编码方式作为 Hash 传递给 attachments 方法。如果您指定了编码方式,Action Mailer 将不会尝试对附件进行 Base64 编码。

3.2 创建内联附件

有时,您可能希望将附件(例如图像)内联发送,使其显示在电子邮件正文中。

要做到这一点,首先,您可以通过调用 #inline 将附件转换为内联附件。

def welcome
  attachments.inline["image.jpg"] = File.read("/path/to/image.jpg")
end

然后在视图中,您可以将 attachments 作为哈希表进行引用,并指定要内联显示的文件。您可以对哈希表调用 url 并将结果传递给 image_tag 方法

<p>Hello there, this is the image you requested:</p>

<%= image_tag attachments['image.jpg'].url %>

由于这是一个对 image_tag 的标准调用,您也可以在附件 URL 之后传递一个选项哈希表。

<p>Hello there, this is our image</p>

<%= image_tag attachments['image.jpg'].url, alt: 'My Photo', class: 'photos' %>

3.3 多部分邮件

创建邮件视图 中所示,如果您对同一个操作有不同的模板,Action Mailer 将自动发送多部分邮件。例如,如果您有一个 UserMailer,其中在 app/views/user_mailer 中包含 welcome_email.text.erbwelcome_email.html.erb,Action Mailer 将自动发送一封包含 HTML 和文本版本的多部分邮件,作为单独的部分。

Mail gem 提供了辅助方法,用于创建用于 text/plaintext/html MIME 类型multipart/alternate 邮件,并且您可以手动创建任何其他类型的 MIME 邮件。

插入部分的顺序由 ActionMailer::Base.default 方法中的 :parts_order 决定。

当您使用电子邮件发送附件时,也会使用多部分邮件。

4 邮件视图和布局

Action Mailer 使用视图文件来指定要发送到电子邮件中的内容。默认情况下,邮件视图位于 app/views/name_of_mailer_class 目录中。与控制器视图类似,文件名称与邮件方法名称匹配。

邮件视图是在布局中渲染的,类似于控制器视图。邮件布局位于 app/views/layouts 中。默认布局为 mailer.html.erbmailer.text.erb。本节介绍邮件视图和布局的各种功能。

4.1 配置自定义视图路径

可以使用多种方法更改操作的默认邮件视图,如下所示。

mail 方法具有 template_pathtemplate_name 选项

class UserMailer < ApplicationMailer
  default from: "[email protected]"

  def welcome_email
    @user = params[:user]
    @url  = "http://example.com/login"
    mail(to: @user.email,
         subject: "Welcome to My Awesome Site",
         template_path: "notifications",
         template_name: "hello")
  end
end

上面的代码配置了 mail 方法,以在 app/views/notifications 目录中查找名为 hello 的模板。您也可以为 template_path 指定路径数组,它们将按顺序被搜索。

如果您需要更多灵活性,您还可以传递一个代码块并渲染特定模板。您也可以在不使用模板文件的情况下,直接渲染纯文本。

class UserMailer < ApplicationMailer
  default from: "[email protected]"

  def welcome_email
    @user = params[:user]
    @url  = "http://example.com/login"
    mail(to: @user.email,
         subject: "Welcome to My Awesome Site") do |format|
      format.html { render "another_template" }
      format.text { render plain: "hello" }
    end
  end
end

这将渲染 another_template.html.erb 模板作为 HTML 部分,并渲染 “hello” 作为文本部分。render 方法与 Action Controller 中使用的方法相同,因此您可以使用所有相同的选项,例如 :plain:inline 等。

最后,如果您需要渲染位于默认 app/views/mailer_name/ 目录之外的模板,您可以使用 prepend_view_path,如下所示

class UserMailer < ApplicationMailer
  prepend_view_path "custom/path/to/mailer/view"

  # This will try to load "custom/path/to/mailer/view/welcome_email" template
  def welcome_email
    # ...
  end
end

还有一个 append_view_path 方法。

4.2 在 Action Mailer 视图中生成 URL

要向邮件添加 URL,首先需要将 host 值设置为应用程序的域名。这是因为与控制器不同,邮件实例没有关于传入请求的任何上下文信息。

您可以在 config/application.rb 中配置应用程序范围内的默认 host

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

配置完 host 之后,建议电子邮件视图使用包含完整 URL 的 *_url,而不是使用包含相对 URL 的 *_path 辅助方法。由于电子邮件客户端没有 Web 请求上下文,因此 *_path 辅助方法没有用于形成完整 Web 地址的基 URL。

例如,不要使用

<%= link_to 'welcome', welcome_path %>

而是使用

<%= link_to 'welcome', welcome_url %>

通过使用完整的 URL,您的链接将在您的电子邮件中正常工作。

4.2.1 使用 url_for 生成 URL

url_for 辅助方法在模板中默认情况下生成完整的 URL。

如果您没有全局配置 :host 选项,则需要将其传递给 url_for

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

4.2.2 使用命名路由生成 URL

与其他 URL 类似,您需要在电子邮件中使用命名路由辅助方法的 *_url 变体。

您可以全局配置 :host 选项,或者确保将其传递给 URL 辅助方法

<%= user_url(@user, host: 'example.com') %>

4.3 在 Action Mailer 视图中添加图像

要使用电子邮件中的 image_tag 辅助方法,您需要指定 :asset_host 参数。这是因为邮件实例没有关于传入请求的任何上下文信息。

通常 :asset_host 在整个应用程序中都是一致的,因此您可以在 config/application.rb 中全局配置它。

config.action_mailer.asset_host = "http://example.com"

由于我们无法从请求推断协议,因此您需要在 :asset_host 配置中指定协议,例如 http://https://

现在您可以在电子邮件中显示图像。

<%= image_tag 'image.jpg' %>

4.4 缓存邮件视图

您可以使用 cache 方法在邮件视图中执行片段缓存,类似于应用程序视图。

<% cache do %>
  <%= @company.name %>
<% end %>

要使用此功能,您需要在应用程序的 config/environments/*.rb 文件中启用它。

config.action_mailer.perform_caching = true

多部分邮件也支持片段缓存。有关缓存的更多信息,请参阅 Rails 缓存指南

4.5 Action Mailer 布局

与控制器布局一样,您也可以拥有邮件布局。邮件布局位于 app/views/layouts 中。以下是默认布局

# app/views/layouts/mailer.html.erb
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <style>
      /* Email styles need to be inline */
    </style>
  </head>

  <body>
    <%= yield %>
  </body>
</html>

上面的布局位于 mailer.html.erb 文件中。默认布局名称是在 ApplicationMailer 中指定的,正如我们在前面的 生成邮件 部分中看到的 layout "mailer" 行。与控制器布局类似,您使用 yield 来在布局中渲染邮件视图。

要为给定的邮件使用不同的布局,请调用 layout

class UserMailer < ApplicationMailer
  layout "awesome" # Use awesome.(html|text).erb as the layout
end

要为给定的电子邮件使用特定布局,您可以将 layout: 'layout_name' 选项传递给格式块中的渲染调用。

class UserMailer < ApplicationMailer
  def welcome_email
    mail(to: params[:user].email) do |format|
      format.html { render layout: "my_layout" }
      format.text
    end
  end
end

上面的代码将使用 my_layout.html.erb 文件渲染 HTML 部分,并使用通常的 user_mailer.text.erb 文件渲染文本部分。

5 发送电子邮件

5.1 发送电子邮件到多个收件人

可以通过将 :to 字段设置为电子邮件地址列表来将电子邮件发送给多个收件人。电子邮件列表可以是数组,也可以是单个字符串,用逗号分隔地址。

例如,要通知所有管理员一个新的注册

class AdminMailer < ApplicationMailer
  default to: -> { Admin.pluck(:email) },
          from: "[email protected]"

  def new_registration(user)
    @user = user
    mail(subject: "New User Signup: #{@user.email}")
  end
end

相同的格式可以用于添加多个抄送 (cc) 和密送 (bcc) 收件人,分别设置 :cc:bcc 键(类似于 :to 字段)。

5.2 发送包含姓名的电子邮件

除了电子邮件地址之外,还可以显示接收电子邮件或发送电子邮件的人员的姓名。

要显示收件人姓名,可以在 to: 中使用 email_address_with_name 方法。

def welcome_email
  @user = params[:user]
  mail(
    to: email_address_with_name(@user.email, @user.name),
    subject: "Welcome to My Awesome Site"
  )
end

from: 中的相同方法可以显示发件人姓名。

class UserMailer < ApplicationMailer
  default from: email_address_with_name("[email protected]", "Example Company Notifications")
end

如果姓名为空(nil 或空字符串),则返回电子邮件地址。

5.3 发送包含主题翻译的电子邮件

如果您没有向 mail 方法传递主题,Action Mailer 将尝试在您的翻译中找到主题。有关更多信息,请参阅 国际化指南

5.4 发送不使用模板渲染的电子邮件

在某些情况下,您可能希望跳过模板渲染步骤,而是提供电子邮件正文作为字符串。您可以使用 :body 选项实现这一点。请记住设置 :content_type 选项,例如在下面将其设置为 text/html。Rails 默认情况下将 text/plain 作为内容类型。

class UserMailer < ApplicationMailer
  def welcome_email
    mail(to: params[:user].email,
         body: params[:email_body],
         content_type: "text/html",
         subject: "Already rendered!")
  end
end

5.5 发送包含动态传递选项的电子邮件

如果您希望在传递电子邮件时覆盖默认的传递 配置(例如 SMTP 凭据),可以使用邮件操作中的 delivery_method_options 来实现。

class UserMailer < ApplicationMailer
  def welcome_email
    @user = params[:user]
    @url  = user_url(@user)
    delivery_options = { user_name: params[:company].smtp_user,
                         password: params[:company].smtp_password,
                         address: params[:company].smtp_host }
    mail(to: @user.email,
         subject: "Please see the Terms and Conditions attached",
         delivery_method_options: delivery_options)
  end
end

6 Action Mailer 回调

Action Mailer 允许您指定一个 before_actionafter_actionaround_action 来配置消息,以及 before_deliverafter_deliveraround_deliver 来控制邮件发送。

回调可以使用代码块或表示邮件类中方法名称的符号来指定,类似于其他回调(在控制器或模型中)。

以下是一些使用这些回调与邮件器的示例。

6.1 before_action

您可以使用 before_action 来设置实例变量,用默认值填充邮件对象,或插入默认的邮件头和附件。

class InvitationsMailer < ApplicationMailer
  before_action :set_inviter_and_invitee
  before_action { @account = params[:inviter].account }

  default to:       -> { @invitee.email_address },
          from:     -> { common_address(@inviter) },
          reply_to: -> { @inviter.email_address_with_name }

  def account_invitation
    mail subject: "#{@inviter.name} invited you to their Basecamp (#{@account.name})"
  end

  def project_invitation
    @project    = params[:project]
    @summarizer = ProjectInvitationSummarizer.new(@project.bucket)

    mail subject: "#{@inviter.name.familiar} added you to a project in Basecamp (#{@account.name})"
  end

  private
    def set_inviter_and_invitee
      @inviter = params[:inviter]
      @invitee = params[:invitee]
    end
end

6.2 after_action

您可以使用 after_action 回调,其设置与 before_action 类似,但还可以访问在邮件器操作中设置的实例变量。

您还可以使用 after_action 通过更新 mail.delivery_method.settings 来覆盖发送方法设置。

class UserMailer < ApplicationMailer
  before_action { @business, @user = params[:business], params[:user] }

  after_action :set_delivery_options,
               :prevent_delivery_to_guests,
               :set_business_headers

  def feedback_message
  end

  def campaign_message
  end

  private
    def set_delivery_options
      # You have access to the mail instance,
      # @business and @user instance variables here
      if @business && @business.has_smtp_settings?
        mail.delivery_method.settings.merge!(@business.smtp_settings)
      end
    end

    def prevent_delivery_to_guests
      if @user && @user.guest?
        mail.perform_deliveries = false
      end
    end

    def set_business_headers
      if @business
        headers["X-SMTPAPI-CATEGORY"] = @business.code
      end
    end
end

6.3 after_deliver

您可以使用 after_deliver 来记录邮件的发送。它还允许观察者/拦截器之类的行为,但可以访问完整的邮件器上下文。

class UserMailer < ApplicationMailer
  after_deliver :mark_delivered
  before_deliver :sandbox_staging
  after_deliver :observe_delivery

  def feedback_message
    @feedback = params[:feedback]
  end

  private
    def mark_delivered
      params[:feedback].touch(:delivered_at)
    end

    # An Interceptor alternative.
    def sandbox_staging
      message.to = ["[email protected]"] if Rails.env.staging?
    end

    # A callback has more context than the comparable Observer example.
    def observe_delivery
      EmailDelivery.log(message, self.class, action_name, params)
    end
end

如果 body 被设置为非空值,邮件器回调将中止进一步处理。before_deliver 可以使用 throw :abort 中止。

7 Action Mailer 视图助手

Action Mailer 视图可以访问与普通视图大部分相同的助手。

还有一些特定于 Action Mailer 的辅助方法可在 ActionMailer::MailHelper 中使用。例如,这些方法允许使用 mailer 从视图中访问邮件器实例,并使用 message 访问邮件。

<%= stylesheet_link_tag mailer.name.underscore %>
<h1><%= message.subject %></h1>

8 Action Mailer 配置

本节显示一些 Action Mailer 的配置示例。

有关各种配置选项的更多详细信息,请参阅 配置 Rails 应用程序 指南。您可以在特定于环境的文件(如 production.rb)中指定配置选项。

8.1 Action Mailer 配置示例

以下是如何在 config/environments/$RAILS_ENV.rb 文件中添加使用 :sendmail 发送方法的示例。

config.action_mailer.delivery_method = :sendmail
# Defaults to:
# config.action_mailer.sendmail_settings = {
#   location: '/usr/sbin/sendmail',
#   arguments: %w[ -i ]
# }
config.action_mailer.perform_deliveries = true
config.action_mailer.raise_delivery_errors = true
config.action_mailer.default_options = { from: "[email protected]" }

8.2 Gmail 的 Action Mailer 配置

将此添加到 config/environments/$RAILS_ENV.rb 文件中,通过 Gmail 发送邮件。

config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
  address:         "smtp.gmail.com",
  port:            587,
  domain:          "example.com",
  user_name:       Rails.application.credentials.dig(:smtp, :user_name),
  password:        Rails.application.credentials.dig(:smtp, :password),
  authentication:  "plain",
  enable_starttls: true,
  open_timeout:    5,
  read_timeout:    5 }

Google 阻止 来自其认为不太安全的应用程序的登录。您可以 更改您的 Gmail 设置 以允许尝试。如果您的 Gmail 帐户启用了双重身份验证,那么您需要设置一个 应用程序密码 并使用它,而不是您的常规密码。

9 预览和测试邮件器

您可以在 测试指南 中找到有关如何测试邮件器的详细说明。

9.1 预览电子邮件

您可以通过访问特殊的 Action Mailer 预览 URL 来以视觉方式预览呈现的电子邮件模板。要为 UserMailer 设置预览,请在 test/mailers/previews/ 目录中创建一个名为 UserMailerPreview 的类。要查看 UserMailerwelcome_email 的预览,请在 UserMailerPreview 中实现具有相同名称的方法并调用 UserMailer.welcome_email

class UserMailerPreview < ActionMailer::Preview
  def welcome_email
    UserMailer.with(user: User.first).welcome_email
  end
end

现在,预览将位于 https://127.0.0.1:3000/rails/mailers/user_mailer/welcome_email 上。

如果您在 app/views/user_mailer/welcome_email.html.erb 中更改邮件器视图或邮件器本身,预览将自动更新。预览列表也位于 https://127.0.0.1:3000/rails/mailers 上。

默认情况下,这些预览类位于 test/mailers/previews 中。可以使用 preview_paths 选项配置它。例如,如果您想将 lib/mailer_previews 添加到其中,您可以在 config/application.rb 中配置它。

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

9.2 救援错误

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

要救援在邮件过程的任何部分发生的错误,请使用 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

10 拦截和观察电子邮件

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

10.1 拦截电子邮件

拦截器允许您在电子邮件传递给发送代理之前对其进行修改。拦截器类必须实现 .delivering_email(message) 方法,该方法将在发送电子邮件之前被调用。

class SandboxEmailInterceptor
  def self.delivering_email(message)
    message.to = ["[email protected]"]
  end
end

拦截器需要使用 interceptors 配置选项注册。您可以在初始化文件(如 config/initializers/mail_interceptors.rb)中执行此操作。

Rails.application.configure do
  if Rails.env.staging?
    config.action_mailer.interceptors = %w[SandboxEmailInterceptor]
  end
end

上面的示例使用了一个名为“staging”的自定义环境,用于生产级别的服务器,但用于测试目的。您可以阅读 创建 Rails 环境,以了解更多关于自定义 Rails 环境的信息。

10.2 观察电子邮件

观察者允许您在电子邮件发送 访问电子邮件消息。观察者类必须实现 :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


返回顶部