设计不通过ActionMailer发送电子邮件

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计不通过ActionMailer发送电子邮件相关的知识,希望对你有一定的参考价值。

我在线阅读了很多教程和答案,但我找不到修复方法。我已经设置了我的ActionMailer来通过Mailgun的服务发送电子邮件。我可以验证在生产中它确实通过我的在线联系表单发送电子邮件而没有任何问题。但是,当我尝试使用Devise的一个恢复模板(即:忘记密码)时,电子邮件不会被发送。

- > config / environments / production.rb

# ActionMailer Config
config.action_mailer.perform_caching = false

config.action_mailer.raise_delivery_errors = true

config.action_mailer.default_url_options = { :host => 'thechristianchain.org' }

config.action_mailer.delivery_method = :smtp
config.action_mailer.perform_deliveries = true

config.action_mailer.default :charset => "utf-8"

config.action_mailer.smtp_settings = {
  :authentication => :plain,
  :address => "smtp.mailgun.org",
  :port => '587',
  :domain => "mx.tccf.co",
  :user_name => ENV["MAILGUN_USERNAME"],
  :password => ENV["MAILGUN_PASSWORD"]
}

要尝试调试,我将raise_deliever_errors设置为true,如上面的代码所示。

当我查看日志时,我看到了这一点(我删除了日志时间戳以便于阅读):

I, [2018-02-23T20:17:28.511441 #4]  INFO -- : [fb02bfdf-a5c3-4918-bae6-5c5d32ef6fba] Sent mail to info@thechristianchain.org (1028.0ms)

D, [2018-02-23T20:17:28.511562 #4] DEBUG -- : [fb02bfdf-a5c3-4918-bae6-5c5d32ef6fba] Date: Fri, 23 Feb 2018 20:17:27 +0000

From: registrations@thechristianchain.org
Reply-To: registrations@thechristianchain.org
To: info@thechristianchain.org
Message-ID: <5a9076d776379_4233fb2465758@2694484e-c3cb-44d0-8ece-832cd70adf2c.mail>
Subject: Reset password instructions
Mime-Version: 1.0
Content-Type: text/html;
 charset=UTF-8
Content-Transfer-Encoding: 7bit

<p>Hello info@thechristianchain.org!</p>

<p>Someone has requested a link to change your password. You can do this through the link below.</p>

<p><a href="http://thechristianchain.org/password/edit?reset_password_token=1gtx9XHmTzUjf97YhJdG">Change my password</a></p>

<p>If you didn't request this, please ignore this email.</p>
<p>Your password won't change until you access the link above and create a new one.</p>

I, [2018-02-23T20:17:28.511950 #4] INFO -- : [fb02bfdf-a5c3-4918-bae6-5c5d32ef6fba] Completed 401 Unauthorized in 1044ms (ActiveRecord: 5.7ms)
F, [2018-02-23T20:17:28.513684 #4] FATAL -- : [fb02bfdf-a5c3-4918-bae6-5c5d32ef6fba]   
F, [2018-02-23T20:17:28.513764 #4] FATAL -- : [fb02bfdf-a5c3-4918-bae6-5c5d32ef6fba] Net::SMTPAuthenticationError (535 5.7.0 Mailgun is not loving your login or password
):

关键是在它失败的最后一行,因为401 UnauthorizedNet::SMTPAuthenticationError并且基本上说明Mailgun不喜欢我的登录凭据。

这是我的问题。 Devise在哪里提取信息?当我通过联系表单发送邮件时,Mailgun会接受我提交的登录凭据;但是当Devise发送时,他们不被接受。这让我觉得某个地方有一个设置不会从production.rb中提取信息。

答案

在处理这个问题4天后,我能够找到解决方案。不确定它是否是最佳或最有说服力的方式,但是为了防止将来有其他人有同样的问题,这就是我所做的。

首先,一些解释。我不知道为什么用我的development.rb更新production.rbconfig.action_mailer.smtp_settings文件不起作用。通过所有文档,似乎Devise应该与这些设置无缝协作。现在,我的联系表格起作用的原因和Devise没有,因为我的联系表格正在使用contact_us_email_mailer.rb,其中包含通过Mailgun API发送电子邮件的代码。 (谢谢@kuwantum。你的评论帮助我建立了以不同方式发送电子邮件的连接。)

所以,我的心态是弄清楚Devise如何发送它的电子邮件,它将SMTP或API称为电子邮件,然后在那里输入我的信息。我去了Devise Wiki,找到了setting up a custom mailer file的“How To”。按照这些步骤,我在membership_mailer.rb下设置app/mailers/文件,如下所示:

class MembershipMailer < Devise::Mailer   

  helper :application
  include Devise::Controllers::UrlHelpers

end

然后通过更改以下行将我的config/initializers/devise.rb文件更改为指向此新邮件:

# Configure the class responsible to send e-mails.
# config.mailer = 'Devise::Mailer'
  config.mailer = 'MembershipMailer'

然后,我需要获取原始的Devise邮件代码并将其输入到本文档中。我得到了code here

设计/ mailer.rb

def confirmation_instructions(record, token, opts={})
  @token = token
  devise_mail(record, :confirmation_instructions, opts)
end

def reset_password_instructions(record, token, opts={})
  @token = token
  devise_mail(record, :reset_password_instructions, opts)
end

def unlock_instructions(record, token, opts={})
  @token = token
  devise_mail(record, :unlock_instructions, opts)
end

def email_changed(record, opts={})
  devise_mail(record, :email_changed, opts)
end

def password_change(record, opts={})
  devise_mail(record, :password_change, opts)
end

看看这段代码,我看到经常调用devise_mail,所以我需要找出代码是什么。我得到了this page。搜索这个网站给了我需要的the code

def devise_mail(record, action, opts = {}, &block)
  initialize_from_record(record)
  mail headers_for(action, opts), &block
end

这段代码然后显示了我在上面提到的网站上搜索的一些函数调用,我需要理解的代码是headers_for部分。这是代码:

def headers_for(action, opts)
  headers = {
    subject: subject_for(action),
    to: resource.email,
    from: mailer_sender(devise_mapping),
    reply_to: mailer_reply_to(devise_mapping),
    template_path: template_paths,
    template_name: action
  }.merge(opts)

  @email = headers[:to]
  headers
end

运行byebug,我能够调用此函数并获得响应的哈希值,并且调用每个函数给了我它的值。所以,输入headers_for(action, opts)[:from]给了我在devise.rb文件中设置的默认电子邮件。所以,这是有效的。

现在,对于那些经过一些试验和错误的部分。我在使用Mailgun的SMTP设置时遇到了困难,但是知道API在处理我的联系表单时工作正常。所以,我需要让变量工作。这是适用于我的联系表单的代码(注意隐藏的密钥是从使用'.env'gem创建的dotenv-rails文件调用的,require语句使用'rest-client'宝石。):

contact_us_email_mailer.rb

require 'rest-client'

def send_email(from, to, subject, text)
  RestClient.post ENV.fetch("MAILGUN_MX_URL"),
    :from => from,
    :to => to,
    :subject => subject,
    :text => text
end

现在,结合使用devise_mail函数,我得到了这段代码:

def mail(record, action, opts = {}, block)
  initialize_from_record(record)

  RestClient.post ENV.fetch("MAILGUN_MX_URL"),
    :from => headers_for(action, opts)[:from],
    :to => headers_for(action, opts)[:to],  
    :subject => headers_for(action, opts)[:subject],
end

然后,我需要弄清楚如何将电子邮件模板创建到电子邮件中。那是当我遇到这个指向我正确方向的StackOverflow answer时,我想出了这个代码:

html_output = render_to_string(:action => action, :layout => false)
:html => html_output.to_str

将它添加到以前的代码我得到了这个:

def mail(record, action, opts = {}, block)
  initialize_from_record(record)
  html_output = render_to_string(:action => action, :layout => false)

  RestClient.post ENV.fetch("MAILGUN_MX_URL"),
    :from => headers_for(action, opts)[:from],
    :to => headers_for(action, opts)[:to],  
    :subject => headers_for(action, opts)[:subject],
    :html => html_output.to_str
end

我必须做的下一个问题是将'action'的输入从符号更改为字符串。例如:confirmation_instructions成为'confirmation_instructions'

最后一个问题是系统试图在membership_mailer文件夹而不是devise/mailers/文件夹中找到所有邮件程序模板。所以,我只是将设计模板移动到新文件夹中,这解决了这个问题。

这样做了。正在调用新的邮件程序,正确发送到Mailgun API,从新文件夹调用设计邮件程序模板并将电子邮件发送给用户。希望这个详细的解释将有助于将来的某些人。

这是最终的邮件代码:

class MembershipMailer < Devise::Mailer   

  helper :application
  include Devise::Controllers::UrlHelpers
  require 'rest-client'

  def mail(record, action, opts = {}, block)
    initialize_from_record(record)
    html_output = render_to_string(:action => action, :layout => false)

    RestClient.post ENV.fetch("MAILGUN_MX_URL"),
      :from => headers_for(action, opts)[:from],
      :to => headers_for(action, opts)[:to],  
      :subject => headers_for(action, opts)[:subject],
      :html => html_output.to_str
  end

  def confirmation_instructions(record, token, opts={})
    @token = token
    mail(record, 'confirmation_instructions', opts)
  end

  def reset_password_instructions(record, token, opts={})
    @token = token
    mail(record, 'reset_password_instructions', opts)
  end

  def unlock_instructions(record, token, opts={})
    @token = token
    mail(record, 'unlock_instructions', opts)
  end

  def email_changed(record, opts={})
    mail(record, 'email_changed', opts)
  end

  def password_change(record, opts={})
    mail(record, 'password_change', opts)
  end
end

以上是关于设计不通过ActionMailer发送电子邮件的主要内容,如果未能解决你的问题,请参考以下文章

ActionMailer 在开发 Rails 4 中不发送邮件

为什么设计不通过gmail smtp发送电子邮件?

使用 ActionMailer 的电子邮件注册确认

使用 ActionMailer 发送多部分邮件时出现问题

如何在 ActionMailer 中捕获错误异常

如何在开发中禁用 ActionMailer?