← 上一章:Model 驗證及回呼 下一章:背景工作及工作排程 →

寄發信件

在 Rails 要寄發信件其實滿容易的,有內建的類別(ActionMailer)可以輕鬆的完成這件事。

寄發信件

在 Rails 內建的產生器中,除了我們常用的 scaffoldcontrollermodel 以及 migration 之外,也可使用 mailer 這個產生器來幫你建立寄信功能所需要的基本架構。首先用 Rails 內建的 mailer 產生器來產生需要的檔案:

$ bin/rails g mailer Contact
    create  app/mailers/contact_mailer.rb
    invoke  erb
    create    app/views/contact_mailer
 identical    app/views/layouts/mailer.text.erb
 identical    app/views/layouts/mailer.html.erb
    invoke  test_unit
    create    test/mailers/contact_mailer_test.rb
    create    test/mailers/previews/contact_mailer_preview.rb

透過 mailer 產生器,建立了一個 ContactMailer 類別以及 app/views/contact_mailer 目錄。先看一下 app/mailers 目錄,現在裡面應該有 2 個檔案,檔名分別是 application_mailer.rbcontact_mailer.rb。打開看一下 application_mailer.rb 檔案的內容:

class ApplicationMailer < ActionMailer::Base
  default from: '[email protected]'
  layout 'mailer'
end

default 方法後面接的 from: '[email protected]',意思就是如果沒有特別指定的話,預設信件的寄件者就是 [email protected]。下一行的 layout 是指會去找 app/views/layouts/mailer 這個樣版。

我們再看一下 contact_mailer.rb 檔案的內容:

class ContactMailer < ApplicationMailer
end

咦?有沒發現好像在哪看過類似的東西?其實 Mailer 的檔案結構,跟 Controller 有點像:

  Controller Mailer
類別 UsersController ContactMailer
繼承類別 ApplicationController ApplicationMailer
檔案位置 app/contollers/users_controller.rb app/mailers/contact_mailer.rb
Layout app/views/layouts/application.html.erb app/views/layouts/mailer.html.erb
View app/views/users/*.erb /app/views/contact_mailer/*.erb

Mailer 上的 action?

既然說 Mailer 跟 Controller 有點像,在 Mailer 也有類似像 action 之類的角色,寫起來大概像這樣:

class ContactMailer < ApplicationMailer
  def say_hello_to(user)
    @user = user
    mail to:@user.email, subject:"你好!!"
  end
end

ContactMailer 類別裡定義了一個叫做 say_hello_to 的方法,並接受一個 user 物件做為參數。其中真正進行寄信的是 mail 那行方法。咦?等等,那信件內容呢?跟 Controller 相比較起來,信件內容大概就是跟 View 差不多的角色。讓我們在 app/views/contact_mailer 目錄裡新增一個跟剛剛這個方法「同名」的檔案(檔案名稱:say_hello_to.html.erb),並且加上以下內容:

<%= @user.name %>,你好:

今天你也有過得開心嗎!

謝謝你的來信,再見!

寫起來的手感是不是跟一般的 View 很像呢?它一樣可以從 Mailer 取得實體變數,並輸出在這個檔案裡。

準備寄發!

準備來寄信吧!我希望可以在成功新增 User 的當下,就寄一封通知信給這位使用者:

class UsersController < ApplicationController
  # ...[略]
  def create
    @user = User.new(user_params)
    if @user.save
      ContactMailer.say_hello_to(@user).deliver_now
      redirect_to @user, notice: 'User was successfully created.'
    else
      render :new
    end
  end
  # ...[略]
end

其中這行:

ContactMailer.say_hello_to(@user).deliver_now

就是寄信的地方了。

有發現奇怪的地方嗎?我們在 ContactMailer 這個類別裡定義實體方法 say_hello_to,為什麼用起來變類別方法了?其實這算是 ActionMailer 幫你做的魔術,只要是繼承自 ActionMailer 的類別,定義在 Mailer 裡的實體方法,都會被轉換成類別方法,使用起來更方便。

試一下:

image

按下送出之後,沒多久就收到信了:

image

收不到信?

如果你是在自己本機上執行寄信,有很高的機會是收不到信的,因為你的本機環境可能沒有可以寄信的設定。實務上,除了使用本機的郵件伺服器來寄信外,也很常使用外部服務來寄信。

然而,我自己不太使用 GMail 當做 SMTP 伺服器來幫忙寄信,因為常常會被歸到垃圾郵件,或是不明原因收不到信,相當不容易查到底是什麼原因造成的。還好目前有第三方的專業寄信服務,以下將使用 Mailgun 為範例

Mailgun 每個月提供了一萬封 Email 寄送的免費額度,如果超過這個數字再付費可獲得更高額度。登入 Mailgun 網站後,在 Domains 選單下可取得一組預設的設定資料:

image

取得 SMTP 以及帳號、密碼資訊之後,接著藉由修改 Rails 的設定檔,可以借用外部的 SMTP(Small Mail Transfer Protocol)伺服器寄信。依據不同的環境(production、development 或 test)修改不同的設定檔,以開發環境(development)舉例,在 Rails.application.configure 區塊裡加上這段設定:

# 檔案 config/environments/development.rb
Rails.application.configure do
  # ...[略]...
  # 加上這段 SMTP 設定
  config.action_mailer.delivery_method = :smtp
  config.action_mailer.smtp_settings = {
    address:              'smtp.mailgun.org',
    port:                 587,
    domain:               '5xruby.tw',
    user_name:            '[email protected]',
    password:             '230dd8f72948443988ec23a43',
    authentication:       'plain',
    enable_starttls_auto: true
  }
end

這樣一來信件的寄達機率會比較高一些,而且通常這些服務都還提供完整的報表供行銷管理人員分析成效。

注意事項:
1. 上面這組帳號密碼在本文公開的時候已無法使用。
2. 你應該把寄信的相關帳號密碼另外找地方存放(例如環境變數),而不是像上面這樣直接寫在程式碼裡面,上面範例這樣寫僅因教學展示目的。

還是一直收到不到啊,怎麼解決?

其實寄發信件一直收不到,特別是用別人家的 SMTP 伺服器,有時候不太容易抓出問題是什麼。除了去看那些提供服務的廠商(例如 Mailgun)的後台是否有提供 log 資訊,也可以試著把設定檔的某個設定打開,以開發環境來說:

# 檔案:config/environments/development.rb

Rails.application.configure do
  # ..略..
  # Don't care if the mailer can't send.
  config.action_mailer.raise_delivery_errors = false

  # ..略..
end

raise_delivery_errors 的值預設是 false,把它改成 true 之後,發生寄件失敗的時候會在 log 裡顯示錯誤的訊息,也許可以比較看得出來是哪邊的問題。

參考資料:

http://guides.rubyonrails.org/action_mailer_basics.html

← 上一章:Model 驗證及回呼 下一章:背景工作及工作排程 →

Comments