更多資訊請參閱 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: "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 內容以傳回給用戶端,而郵件程式則建立要透過電子郵件傳遞的訊息。

讓我們將一個名為 welcome_email 的方法新增至 UserMailer,該方法會將電子郵件傳送至使用者的註冊電子郵件地址

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

您也可以從單一郵件程式類別傳送多封電子郵件。將相關電子郵件分組在一起可能很方便。例如,除了 welcome_email 之外,上面的 UserMailer 還可以有一個 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 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 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,而第一部分為 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 將會自動傳送多部分電子郵件。例如,如果您的 UserMailerapp/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: "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

若要對指定的電子郵件使用特定的版面配置,您可以將 layout: 'layout_name' 選項傳遞至格式區塊內的 render 呼叫

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 寄送帶有主旨翻譯的電子郵件

如果您沒有將主旨傳遞給 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 憑證),您可以在 mailer 動作中使用 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 來控制傳遞。

回呼可以使用區塊或代表 mailer 類別中方法名稱的符號來指定,與其他回呼(在控制器或模型中)類似。

以下是一些您可能在 mailer 中使用這些回呼的範例。

6.1 before_action

您可以使用 before_action 來設定實例變數、以預設值填入 mail 物件,或插入預設標頭和附件。

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 回呼,但也可以存取在 mailer 動作中設定的實例變數。

您也可以使用 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 來記錄訊息的傳遞。它也允許類似觀察者/攔截器的行為,但可以存取完整的 mailer 內容。

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 值,Mailer 回呼會中止進一步處理。before_deliver 可以使用 throw :abort 中止。

7 Action Mailer 檢視輔助方法

Action Mailer 檢視可以存取與一般檢視相同的大部分輔助方法。

ActionMailer::MailHelper 中也有一些 Action Mailer 特有的輔助方法可用。例如,這些方法允許您從檢視中透過 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 預覽和測試 Mailer

您可以在測試指南中找到有關如何測試 mailer 的詳細說明。

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 的 mailer 檢視或 mailer 本身中變更任何內容,預覽將會自動更新。預覽的清單也可以在 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 救援錯誤

mailer 方法內部的 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 提供進入 Mail 觀察器和攔截器方法的掛鉤。這些掛鉤允許您註冊在傳送每封電子郵件的郵件傳遞生命週期中呼叫的類別。

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


回到頂端