ActionMailer:__E-mail发送
ActionMailer - E-mail 发送
Talk is cheap. Show me the code. - Linus Torvalds
举凡使用者注册认证信、忘记密码通知信、电子报、各种讯息通知, E-mail 寄送都是现代网站必备的一项功能。 Rails 的 ActionMailer 元件提供了很方便的 Email 整合。
ActionMailer设定
Rails 在 config/environments 目录下针对不同执行环境会有不同的邮件服务器设定。在 development.rb 开发模式中,以下设定会忽略任何寄信的错误:
## Don't care if the mailer can't send.
config.action_mailer.raise_delivery_errors = false
建议可以改成 true,这样可以提早发现错误。
寄信方式的选项包括有:test、:sendmail和smtp三种可以选择。sendmail是使用服务器的 /usr/bin/sendmail 程式,不过因为因为不是每台服务器都有适当安装 sendmail 。而:test代表并不会实际寄信出去,而是存在ActionMailer::Base.deliveries阵列中方便做自动化测试。
最推荐的方式是采用:smtp协定来实际寄信出去,例如以下是一个使用 Gmail 寄信的范例,请修改 config/environments/development.rb 或 config/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 _comment_ 的_Action ,其 template 在 app/views/user_mailer/notify_comment.text.erb(纯文字格式)和 notify_comment.html.erb(HTML格式)。如果两种格式的样板档案都有,那么 Rails 会合并成一封 Multiple Content Types 的 Email 。
让我们看看 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.erb 或 app/views/user_mailer/notify_comment.html.erb 或样板中存取到。而 mail 方法则还可以接受其他参数包括cc、bcc。
我们可以在 rails console 中测试,执行UserMailer.notify _comment(user, comment).deliver_now!_就会寄信出去。(这里我们假设存在一个_user 和 comment 物件代表使用者和新留言,例如user = User.first和comment = 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 也可以办到收信,但是你需要自行架设邮件服务器。因此需要这个功能的话,也会使用第三方服务,例如mailgun和MailChimp都有提供收信的 Webhook 服务:信寄到第三方服务,然后第三方再呼叫网站的 HTTP API ,这样就省去你自己架设邮件服务器的困难。
