Ruby on Rails 實戰聖經

使用 Rails 5.0+ 及 Ruby 2.3+

電子書製作中,歡迎留下 E-mail,有消息將會通知您。若您有任何意見、鼓勵或勘誤,也歡迎來信給我。願意贊助支持的話,这是我的支付宝微信 和乙太幣 ETH 地址
0x232b7245EBE02900c21682be1e6Ad4e839751F6a

ActionMailer - E-mail 發送

Talk is cheap. Show me the code. - Linus Torvalds

舉凡使用者註冊認證信、忘記密碼通知信、電子報、各種訊息通知,E-mail寄送都是現代網站必備的一項功能。RailsActionMailer元件提供了很方便的Email整合。

ActionMailer設定

Railsconfig/environments目錄下針對不同執行環境會有不同的郵件伺服器設定。在development.rb開發模式中,以下設定會忽略任何寄信的錯誤:

# Don't care if the mailer can't send.
config.action_mailer.raise_delivery_errors = false

建議可以改成 true,這樣可以提早發現錯誤。

寄信方式的選項包括有:test:sendmailsmtp三種可以選擇。sendmail是使用伺服器的/usr/bin/sendmail程式,不過因為因為不是每台伺服器都有適當安裝sendmail。而:test代表並不會實際寄信出去,而是存在ActionMailer::Base.deliveries陣列中方便做自動化測試。

最推薦的方式是採用:smtp協定來實際寄信出去,例如以下是一個使用Gmail寄信的範例,請修改config/environments/development.rbconfig/environments/production.rb

config.action_mailer.delivery_method = :smtp
config.action_mailer.default_url_options = { host: "http://localhost:3000" }
config.action_mailer.smtp_settings = {
    :address => "smtp.gmail.com",
    :port => "587",
    :domain => "gmail.com",
    :authentication => "plain",
    :user_name => "example@gmail.com",
    :password => "123456",
    :enable_starttls_auto => true
 }

其中default_url_options設定是因為在Email這個情境下,如果要在Email中放超連結,必須是絕對網址。所以我們必須設定網站的網址。

另外實務上,我們其實並不會將帳號密碼寫死進程式裡面,而是希望拆出來另存一個設定檔。例如我們可以放到config/email.yml如下,YAML第一層是適用的Rails環境:

development:
  address: "smtp.gmail.com"
  port: 587
  domain: "gmail.com"
  authentication: "plain"
  user_name: "example@gmail.com"
  password: "123456"
  enable_starttls_auto: true
production:
  address: "smtp.mailgun.org"
  port: 587
  domain: "ihower.com"
  authentication: "plain"
  user_name: "postmaster@ihower.tw"
  password: "1234567890"
  enable_starttls_auto: true

這樣的話,smtp_settings就可以改成:

config.action_mailer.smtp_settings = config_for(:email).symbolize_keys

其中config_for這個方法會讀取config目錄下的YAML設定檔,並根據當時的Rails啟動環境。而symbolize_keys這個方法會將Hash中的String key換成Symbol key,這是因為smtp_settings吃的是Symbol key,如果沒有轉的話,會讀不到設定。

通常config/email.yml會加到你的.gitignore列表中,讓git忽略不要commit這個檔案,因為有帳號密碼在裡面。

建立一個Mailer寄信程式

Controller一樣,Rails也用generate指令產生Mailer類別,此類別中的一個方法就對應一個Email樣板。以下是一個產生Mailer的範例:

rails generate mailer UserMailer notify_comment

如此便會產生app/mailers/user_mailer.rb檔案,並包含一個notify_commentAction,其templateapp/views/user_mailer/notify_comment.text.erb(純文字格式)和notify_comment.html.erb(HTML格式)。如果兩種格式的樣板檔案都有,那麼Rails會合併成一封Multiple Content TypesEmail

讓我們看看 user_mailer.rb 的程式:

class UserMailer < ActionMailer::Base
    default :from => "寄件人名字 <noreply@example.org>"

    def notify_comment(user, comment)
        @comment = comment
        mail(:to => user.email, :subject => "New Comment")
    end
end

其中default方法可以設定預設的寄件人。而 mail 方法可以設定收件人和郵件主旨。和View一樣,@user物件變數可以在app/views/user_mailer/notify_comment.text.erbapp/views/user_mailer/notify_comment.html.erb或樣板中存取到。而mail方法則還可以接受其他參數包括ccbcc

我們可以在rails console中測試,執行UserMailer.notify_comment(user, comment).deliver_now!就會寄信出去。(這裡我們假設存在一個usercomment物件代表使用者和新留言,例如user = User.firstcomment = Comment.last)

實務上,我們會在controller之中,例如使用者張貼留言之後寄發信件:

def create
  comment = Comment.new(comment_params)
  if comment.save
    UserMailer.notify_comment(current_user, comment).deliver_later!
    redirect_to comments_path
  else
    render :action => :new
  end
end

如果只需要純文字版,就砍掉app/views/user_mailer/notify_comment.html.erb這個檔案,然後在app/views/user_mailer/notify_comment.text.erb純文字格式中,可以加入以下文字跟網址:

有新留言在 <%= comments_url %>

另外,因為寄信這個動作比較耗時,通常我們也會搭配使用非同步的機制,因此上述用法分成了deliver_now!deliver_later!兩種,而後者就會搭配ActiveJob進行非同步的寄送,我們在非同步一章會詳細介紹如何設定。

Helper 的使用

在 email 樣本中,預設是不會載入 app/helpers 裡面的 Helper 方法的,如果你要使用的話,可以在該 Mailer 類別中宣告如下:

class UserMailer < ApplicationMailer
  helper :application # 這樣會載入 app/helpers/application_helper.rb
  helper :users     # 這樣會載入 app/helpers/users_helper.rb
  # ...
 end

開發預覽

開發期間我們需要常常測試預覽寄出的Email內容,但是實際寄送出去又很沒效率。我們可以安裝letter_opener這個gem,修改Gemfile加入:

gem "letter_opener", :group => :development

然後將config.action_mailer.delivery_method改成:letter_opener

這樣在開發模式下,就會開瀏覽器進行預覽,而不會真的寄信出去。

第三方寄信服務

由於Gmail是個人用途使用,用量有限,並不適合開站做生意使用。我們實務上我們會使用第三方服務來確保Email遞送的可靠性,例如:

大量寄送 Email 會是一門學問,請參考 如何正確發送(大量) Email 信件 這篇文章

Email CSS 處理

  • Email 會被各種奇形怪狀的閱讀器所瀏覽,這些環境中的 CSS 支援非常受限:
    • https://blog.othree.net/log/2016/08/25/modern-html-email-develop/
    • https://www.campaignmonitor.com/css/
    • http://templates.mailchimp.com/development/css/
  • 解法: 需要將 CSS inline 內嵌到 HTML 裡面
    • 安裝 https://github.com/fphilipe/premailer-rails

我們也可以套現成的 Email Responsive Template 樣板,例如:

  • http://blog.mailgun.com/transactional-html-email-templates/
  • https://github.com/leemunroe/responsive-html-email-template
  • https://htmlemail.io/ $49

收信

Active Mailer也可以辦到收信,但是你需要自行架設郵件伺服器。因此需要這個功能的話,也會使用第三方服務,例如mailgunMailChimp都有提供收信的Webhook服務:信寄到第三方服務,然後第三方再呼叫網站的HTTP API,這樣就省去你自己架設郵件伺服器的困難。

更多線上資源

》回到頁首