我在线阅读了很多教程和答案,但我找不到修复方法。我已经设置了我的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 [email protected] (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: [email protected]
Reply-To: [email protected]
To: [email protected]
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 [email protected]!</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 Unauthorized
和Net::SMTPAuthenticationError
并且基本上说明Mailgun不喜欢我的登录凭据。
题
这是我的问题。 Devise在哪里提取信息?当我通过联系表单发送邮件时,Mailgun会接受我提交的登录凭据;但是当Devise发送时,他们不被接受。这让我觉得某个地方有一个设置不会从production.rb
中提取信息。
在处理这个问题4天后,我能够找到解决方案。不确定它是否是最佳或最有说服力的方式,但是为了防止将来有其他人有同样的问题,这就是我所做的。
首先,一些解释。我不知道为什么用我的development.rb
更新production.rb
和config.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