← 上一章: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_mail.erb 檔案的內容:

  class ApplicationMailer < ActionMailer::Base
    default from: 'from@example.com'
    layout 'mailer'
  end

default 方法後面接的 from: 'from@example.com',意思就是如果沒有特別指定的話,預設信件的寄件者就是 from@example.com。下一行的 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:            'postmaster@sandbox230dd8f72948443988ec23a4339b9745.mailgun.org',
      password:             '230dd8f72948443988ec23a43',
      authentication:       'plain',
      enable_starttls_auto: true
    }
  end

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

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

參考資料:

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

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

Comments