我有一个可以处理多个子域的rails应用程序,我有多个运行不同域名的实时版本。这会导致URL位于
之间的任何位置mywebsite.com
company1.mywebsite.com
company1.mytestwebsite.com
Devise有使用链接重置密码等的邮件程序。这些链接有时不正确并将我发送到不正确的网站,因为主机名有时与/config/environments/production.rb
的默认网址不同:
config.action_mailer.default_url_options = { host: 'mywebsite.com' }
如何将request.host
从控制器传递到Devise邮件程序?
如果我有主持人,我可以创建将用户发送到正确网站的链接
答案 0 :(得分:4)
只需将一个文件添加到/config/initializers
并覆盖设计控制器
文件:
# config/initializers/devise_monkeypatch.rb
module Devise
module Models
module Recoverable
module ClassMethods
# extract data from attributes hash and pass it to the next method
def send_reset_password_instructions(attributes = {})
data = attributes.delete(:data).to_unsafe_h
recoverable = find_or_initialize_with_errors(reset_password_keys, attributes, :not_found)
recoverable.send_reset_password_instructions(data) if recoverable.persisted?
recoverable
end
end
# adjust so it accepts data parameter and sends it to next method
def send_reset_password_instructions(data)
token = set_reset_password_token
send_reset_password_instructions_notification(token, data)
token
end
# adjust so it accepts data parameter and sends to next method
protected def send_reset_password_instructions_notification(token, data)
send_devise_notification(:reset_password_instructions, token, data: data)
end
end
end
Mailer.class_eval do
# extract data from options and set it as instance variable
def reset_password_instructions(record, token, opts={})
@token = token
@data = opts.delete :data
devise_mail(record, :reset_password_instructions, opts)
end
end
end
生成控制器和视图
rails g devise:controllers users -c=passwords
rails g devise:views
编辑路线
# config/routes.rb
devise_for :users, controllers: {
passwords: 'users/passwords'
}
编辑创建操作
class Users::PasswordsController < Devise::PasswordsController
def create
params[:user][:data] = { host: request.url.remove(request.path) }
super
end
end
编辑视图
<p>
<%= link_to 'Change my password',
edit_password_url(
@resource, reset_password_token: @token, host: @data[:host]
)
%>
</p>
以下说明:
查看source code,这些是用于从控制器到邮件程序的方法
# the controller calls send_reset_pasword_instructions on class
Devise::PasswordsController#create
resource_class.send_reset_password_instructions(resource_params)
# class finds instance and calls send_reset_password_instructions on instance
Devise::Models::Recoverable::ClassMethods
def send_reset_password_instructions(attributes = {})
recoverable = find_or_initialize_with_errors(reset_password_keys, attributes, :not_found)
recoverable.send_reset_password_instructions if recoverable.persisted?
recoverable
end
Devise::Models::Recoverable
# instance calls send_reset_password_instructions_notification
def send_reset_password_instructions
token = set_reset_password_token
send_reset_password_instructions_notification(token)
token
end
# instance calls send_devise_notification
protected def send_reset_password_instructions_notification(token)
send_devise_notification(:reset_password_instructions, token, {})
end
Devise::Models::Authenticatable
# instance calls mailer
protected def send_devise_notification(notification, *args)
message = devise_mailer.send(notification, self, *args)
# Remove once we move to Rails 4.2+ only.
if message.respond_to?(:deliver_now)
message.deliver_now
else
message.deliver
end
end
# mailer
protected def devise_mailer
Devise.mailer
end
class Devise::Mailer
# mailer sets @token
def reset_password_instructions(record, token, opts={})
@token = token
devise_mail(record, :reset_password_instructions, opts)
end
您只需要在最后一个方法中设置另一个实例变量,但您必须编辑其他方法才能成功传递数据。
以下是原始版本与所需更改的比较:
Devise::PasswordsController
# original, will stay the same
def create
self.resource = resource_class.send_reset_password_instructions(resource_params)
yield resource if block_given?
if successfully_sent?(resource)
respond_with({}, location: after_sending_reset_password_instructions_path_for(resource_name))
else
respond_with(resource)
end
end
# override to add data
def create
params[:user][:data] = request.url
super
end
Devise::Models::Recoverable::ClassMethods
# original, will be overwritten
def send_reset_password_instructions(attributes = {})
recoverable = find_or_initialize_with_errors(reset_password_keys, attributes, :not_found)
recoverable.send_reset_password_instructions if recoverable.persisted?
recoverable
end
# extract data from attributes hash and pass it to the next method
def send_reset_password_instructions(attributes = {})
data = attributes.delete :data
recoverable = find_or_initialize_with_errors(reset_password_keys, attributes, :not_found)
recoverable.send_reset_password_instructions(data) if recoverable.persisted?
recoverable
end
Devise::Models::Recoverable
# original, will be overwritten
def send_reset_password_instructions
token = set_reset_password_token
send_reset_password_instructions_notification(token)
token
end
# adjust so it accepts data parameter and sends it to next method
def send_reset_password_instructions(data)
token = set_reset_password_token
send_reset_password_instructions_notification(token, data)
token
end
# original, will be overwritten
protected def send_reset_password_instructions_notification(token)
send_devise_notification(:reset_password_instructions, token, {})
end
# adjust so it accepts data parameter and sends to next method
protected def send_reset_password_instructions_notification(token, data)
send_devise_notification(:reset_password_instructions, token, data: data)
end
Devise::Models::Authenticatable
# original, stays the same
protected def send_devise_notification(notification, *args)
message = devise_mailer.send(notification, self, *args)
# Remove once we move to Rails 4.2+ only.
if message.respond_to?(:deliver_now)
message.deliver_now
else
message.deliver
end
end
# original, stays the same
protected def devise_mailer
Devise.mailer
end
class Devise::Mailer
# extract data from options and set it as instance variable
def reset_password_instructions(record, token, opts={})
@token = token
@data = opts.delete[:data]
devise_mail(record, :reset_password_instructions, opts)
end
删除保持不变的代码以及控制器代码,因为我们将在另一个文件中更改它。将它包装成一个整洁的小模块并进行调整,以便add methods to classes我们传递Hash个对象而不是Parameter个对象。
这是最终版本
module Devise
module Models
module Recoverable
module ClassMethods
# extract data from attributes paramater object and convert it to hash
# and pass it to the next method
def send_reset_password_instructions(attributes = {})
data = attributes.delete(:data).to_unsafe_h
recoverable = find_or_initialize_with_errors(reset_password_keys, attributes, :not_found)
recoverable.send_reset_password_instructions(data) if recoverable.persisted?
recoverable
end
end
# adjust so it accepts data parameter and sends it to next method
def send_reset_password_instructions(data)
token = set_reset_password_token
send_reset_password_instructions_notification(token, data)
token
end
# adjust so it accepts data parameter and sends to next method
protected def send_reset_password_instructions_notification(token, data)
send_devise_notification(:reset_password_instructions, token, data: data)
end
end
end
Mailer.class_eval do
# extract data from options and set it as instance variable
def reset_password_instructions(record, token, opts={})
@token = token
@data = opts.delete :data
devise_mail(record, :reset_password_instructions, opts)
end
end
end
自己回答这个问题,因为即使有others who also asked about this,我也无法在线找到解决方案。在Stack Overflow上发布此内容,以最大限度地帮助其他人解决同样的问题。