← 上一章:Model 驗證及回呼 下一章:背景工作及工作排程 →
寄發信件
在 Rails 要寄發信件其實滿容易的,有內建的類別(ActionMailer)可以輕鬆的完成這件事。
寄發信件
在 Rails 內建的產生器中,除了我們常用的 scaffold
、controller
、model
以及 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.rb
跟 contact_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 裡的實體方法,都會被轉換成類別方法,使用起來更方便。
試一下:
按下送出之後,沒多久就收到信了:
收不到信?
如果你是在自己本機上執行寄信,有很高的機會是收不到信的,因為你的本機環境可能沒有可以寄信的設定。實務上,除了使用本機的郵件伺服器來寄信外,也很常使用外部服務來寄信。
然而,我自己不太使用 GMail 當做 SMTP 伺服器來幫忙寄信,因為常常會被歸到垃圾郵件,或是不明原因收不到信,相當不容易查到底是什麼原因造成的。還好目前有第三方的專業寄信服務,以下將使用 Mailgun 為範例
Mailgun 每個月提供了一萬封 Email 寄送的免費額度,如果超過這個數字再付費可獲得更高額度。登入 Mailgun 網站後,在 Domains 選單下可取得一組預設的設定資料:
取得 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 裡顯示錯誤的訊息,也許可以比較看得出來是哪邊的問題。
Comments