更多内容请访问 rubyonrails.org:

1. 什么是 Action Mailer?

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

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

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

2. 创建邮件程序和视图

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

2.1. 生成邮件程序

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

$ 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: "from@example.com"
  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: "notifications@example.com"

  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

以上将生成带有一个空的welcome_email方法的UserMailer

您还可以从单个邮件程序类发送多封电子邮件。将相关电子邮件分组在一起可能很方便。例如,上面的UserMailer除了welcome_email之外,还可以有一个goodbye_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 log in to the site, just follow this link: <%= link_to 'login', login_url %>.
</p>
<p>Thanks for joining and have a great day!</p>

以上是标签的内容。它将嵌入到包含标签的默认邮件程序布局中。有关更多信息,请参阅邮件程序布局

您还可以创建上述电子邮件的文本版本,并将其存储在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 log in 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 6661f55087e34_1380c7eb86934d@Bhumis-MacBook-Pro.local.mail (19.9ms)
[ActiveJob] [ActionMailer::MailDeliveryJob] [ec4b3786-b9fc-4b5e-8153-9153095e1cbf] Date: Thu, 06 Jun 2024 12:43:44 -0500
From: notifications@example.com
To: test@gmail.com
Message-ID: <6661f55087e34_1380c7eb86934d@Bhumis-MacBook-Pro.local.mail>
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

如果您想立即发送电子邮件(例如从 cronjob),您可以调用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: "hi@gmail.com",
        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 附件。Action Mailer 将自动猜测mime_type,设置encoding,并创建附件。

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

mail方法被触发时,它将发送一封带有附件的多部分电子邮件,其顶级部分为multipart/mixed,第一部分为包含纯文本和 HTML 电子邮件消息的multipart/alternative

发送附件的另一种方法是指定文件名、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 会自动发送多部分电子邮件。例如,如果您在app/views/user_mailer中有UserMailerwelcome_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: "notifications@example.com"

  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: "notifications@example.com"

  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

这将渲染 HTML 部分的模板another_template.html.erb,以及文本部分的“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助手没有基本 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

要为给定电子邮件使用特定布局,您可以在 format 块内的 render 调用中传入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: "notification@example.com"

  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("notification@example.com", "Example Company Notifications")
end

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

5.3. 发送带有主题翻译的电子邮件

如果您没有向邮件方法传递主题,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 允许您指定*_action回调来配置消息,以及*_deliver回调来控制投递。

以下是所有可用的 Action Mailer 回调列表,它们在发送电子邮件时将按照调用的顺序排列

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

以下是一些何时将这些回调与邮件程序一起使用的示例。

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

您可以使用与before_action类似设置的after_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 = ["sandbox@example.com"] 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被设置为非 nil 值,邮件程序回调将中止后续处理。before_deliver可以通过throw :abort中止。

7. Action Mailer 视图助手

Action Mailer 视图可以访问与常规视图相同的大多数助手。

ActionMailer::MailHelper中也提供了一些 Action Mailer 特定的辅助方法。例如,这些方法允许您使用mailer从视图中访问邮件程序实例,并使用message访问消息

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

8. Action Mailer 配置

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

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

8.1. Action Mailer 配置示例

这是一个使用:sendmail投递方法的示例,添加到config/environments/$RAILS_ENV.rb文件中

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: "no-reply@example.com" }

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://:3000/rails/mailers/user_mailer/welcome_email可用。

如果您更改app/views/user_mailer/welcome_email.html.erb中的邮件程序视图或邮件程序本身,预览将自动更新。预览列表也可在https://: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 块无法 rescue 渲染之外发生的错误。例如,后台作业中的记录反序列化错误,或来自第三方邮件投递服务的错误。

为了 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

10. 拦截和观察电子邮件

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

10.1. 拦截电子邮件

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

class SandboxEmailInterceptor
  def self.delivering_email(message)
    message.to = ["sandbox@example.com"]
  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


回到顶部