我正在编写一个小型 Rails 应用程序,我正在尝试添加让用户以类似于 rails cast 274 的方法重置密码的功能(http://railscasts.com/episodes/274-remember-me-reset-password)。我能够添加此功能,但现在似乎已损坏。我有一个来自登录页面的 reset_password 表单的链接。导轨代码如下:
<h1>Reset Password</h1>
<%= form_tag password_resets_path, :method => :post do %>
<div class="field">
<%= label_tag :email %>
<%= text_field_tag :email, params[:email] %>
</div>
<div class="actions"><%= submit_tag "Reset Password" %></div>
<% end %>
下面是浏览器生成的html(密码重置表单,不是页面源)。
<form accept-charset="UTF-8" action="/password_resets" method="post"><div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="✓" /><input name="authenticity_token" type="hidden" value="1UwA3fS90cean/z/g60M3CJYs1RNtHjyy4a1yAssWo0=" /></div>
<div class="field">
<label for="email">Email</label>
<input id="email" name="email" type="text" />
</div>
<div class="actions"><input name="commit" type="submit" value="Reset Password" /></div>
</form>
我对 rails 和 html 有点陌生,但我认为这个表单通过“post”方法将数据传递给我的控制器。我认为这就是我想要的,因为运行 rake 路由给了我(仅用于密码重置 - 我还有一个用户和会话资源):
users GET /users(.:format) users#index
password_resets GET /password_resets(.:format) password_resets#index
POST /password_resets(.:format) password_resets#create
new_password_reset GET /password_resets/new(.:format) password_resets#new
edit_password_reset GET /password_resets/:id/edit(.:format) password_resets#edit
password_reset GET /password_resets/:id(.:format) password_resets#show
PUT /password_resets/:id(.:format) password_resets#update
DELETE /password_resets/:id(.:format) password_resets#destroy
所以我认为提交这个表单应该让我在我的 password_resets 控制器上进行创建操作。但是,我得到:
Routing Error
No route matches {:action=>"edit", :controller=>"password_resets", :id=>nil}
这意味着表单以某种方式尝试提交到编辑操作。如果是这种情况,id=>nil 是有意义的,因为我的表单要求的是 :email,而不是 :id。我不确定为什么/如何发生这种情况,因为我将 password_resets 视为一种资源(如在 rails cast 中),并且只是使用 rails 自动生成的路线。
下面是我的 routes.rb 文件和我的 password_resets 控制器文件。
MyApp::Application.routes.draw do
resources :users
resources :sessions, only: [:new, :create, :destroy]
resources :password_resets
root to: 'static_pages#home'
match '/help', to: 'static_pages#help'
match '/about', to: 'static_pages#help'
match '/signup', to: 'users#new'
match '/remove', to: 'users#remove'
match '/signin', to: 'sessions#new'
match '/signout', to: 'sessions#destroy', via: :delete
end
和 password_reset 控制器:
class PasswordResetsController < ApplicationController
def new
end
def create
user = User.find_by_email(params[:email])
user.send_password_reset if user
#this will work regardless of whether user is found
#a bit more secure, but also a bit more annoying to the
#user...
redirect_to root_url, :notice =>
"Email was sent with password reset
instructions"
end
def edit
@user = User.find_by_password_reset_token!(params[:id])
end
def update
@user = User.find_by_password_reset_token!(params[:id])
if @user.password_reset_sent_at < 2.hours.ago
redirect_to new_password_reset_path, :alert =>
"Password reset has expired."
elsif @user.update_attributes(params[:user])
redirect_to root_url, :notice => "Password has been reset!"
else
render :edit
end
end
end
我还创建了一个动作邮件程序来将电子邮件发送给用户,但我认为还没有必要去看看那里,因为在我得到任何涉及实际发送电子邮件的代码之前,我已经崩溃了。我真的很感激任何关于为什么我的表格表现得如此的想法/猜测,而不是我想要的样子。
提前感谢大家。
更新:
谢谢皮质!正如建议的那样,我查看了服务器日志,我的表单正在执行创建操作,并且代码正在生成电子邮件。但是,我在电子邮件中将密码重置令牌作为用户重置密码的 url 的一部分提供给用户,始终为零,这给了我错误。我通过以下代码生成令牌(我认为与 Rails Cast 相同)
def generate_token(column)
begin
self[column] = SecureRandom.urlsafe_base64
end while User.exists?(column => self[column])
end
我尝试通过以下方式将其分配给用户:
def send_password_reset
self.password_reset_token = generate_token(:password_reset_token)
self.password_reset_sent_at = Time.zone.now
self.save(validate: false)
UserMailer.password_reset(self).deliver
end
所以我现在的问题是为什么我的生成令牌方法会返回一个 nil 值?这很令人费解,因为我使用 generate token 方法来生成一个令牌来跟踪整个会话中的用户,并且它在那里工作得很好......
弥敦道