本人在看《Ruby on Rails Tutorial(第二版)》学习 Rails。看完第八章登陆和退出,自己敲入里面的代码,运行测试一切正常。
但是我在看第九章时, 点击页面上的退出按钮直接跳转到首页但却没有退出,右上角还是可以看到“Account”,有下列菜单。 我查看了退出的代码,没有发现什么问题。
另外, 直接输入登陆链接,登陆进去之后,能登陆进去,但是右上角却还是显示“Sign in”。
请帮忙看看这是怎么回事?谢谢!
另外,如何 单步调试 Rails 程序?需要什么工具?怎么整?在 RubyMine 能否可以?还请指点一下。谢谢!
代码: https://github.com/diguage/rortutorial
《Ruby on Rails Tutorial(第二版)》中文版: http://railstutorial-china.org/
User.all.each { |user| user.save(validate: false) }
#5 楼 @reyesyang
我查看了一下 SQLite,remember_token 那列是空。但是,我看代码和没错。
在/app/models/user.rb
中的生成 remember_token 的代码没有错啊!如下:
class User < ActiveRecord::Base
attr_accessible :email, :name, :password, :password_confirmation
has_secure_password
before_save { |user| user.email = email.downcase }
before_save :create_remember_token
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\-.]+\.[a-z]+\z/i
validates :email, presence: true,
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :password, presence: true, length: { minimum: 6 }
validates :password_confirmation, presence: true
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
end
#7 楼 @andor_chen
干嘛要执行User.all.each { |user| user.save(validate: false) }
这个呢?在哪里执行啊?
我查看一下浏览器的 Cookie 啊。
手机上不好查看代码,目测是 strong parameter 的问题,rails 3 的话就是 attr_accessible 的问题。
额... 搞错了,不是这问题
最后,楼主需要冷静分析问题,很多时候是因为粗心或者不知其原理而想当然写出的代码而导致的问题。最好能提供相关代码段,不要整篇都贴。
remember_token
有值,如果为空;rails s
进入控制台,执行User.all.each { |user| user.save(validate: false) }
生成remember_token
#15 楼 @Ryan #16 楼 @xuncheng #17 楼 @blacktulip #7 楼 @andor_chen #5 楼 @reyesyang #6 楼 @cqcn1991
谢谢各位!问题貌似解决了。原因是,我在加入 remember_token 前,创建了俩个用户。所以已有用户中 remember_token 就为空。然后使用rake db:reset
重新生成数据库表结构。重新注册,就有了 remember_token。再次登陆就正常了。退出也没有问题了。
不过,还是有疑问。
先从创建用户说起,表单不提,只展示一下,创建 remember_token 的代码,如下(以下代码第一行是代码的路径。):
# /app/models/user.rb
class User < ActiveRecord::Base
# …
before_save :create_remember_token
# …
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
end
登陆表单如下:
<!-- /app/views/sessions/new.html.erb -->
<%= form_for(:session, url: sessions_path) do |f| %>
<%= f.label :email %>
<%= f.text_field :email %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.submit "Sign in", class: "btn bin-large btn-primary" %>
<% end %>
登陆的 Action 代码如下:
# /app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
def create
user = User.find_by_email(params[:session][:email])
if user && user.authenticate(params[:session][:password])
sign_in user
redirect_to user
else
flash.now[:error] = 'Invalid email/password combination' # Not quite right!
render 'new'
end
end
end
登录的辅助方法:
# /app/helpers/sessions_helper.rb
module SessionsHelper
def sign_in(user)
cookies.permanent[:remember_token] = user.remember_token
self.current_user = user
end
def current_user=(user)
@current_user = user
end
end
登陆之后,在页面 header 中判断是否登陆的代码入下:
<!-- /app/views/layouts/_header.html.erb -->
<% if signed_in? %>
<li><%= link_to "Users", '#' %></li>
<li id="fat-menu" class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
Account <b class="caret"></b></a>
<!--#……-->aa
<% else %>
<li><%= link_to "Sign in", signin_path %></li>
<% end %>
判断是否登陆的辅助方法 signed_in?代码如下:
# /app/helpers/sessions_helper.rb
module SessionsHelper
def signed_in?
!current_user.nil?
end
def current_user
@current_user ||= User.find_by_remember_token(cookies[:remember_token])
end
#……
end
疑问一:即使表中有俩个记录 remember_token 一样时,登陆之后,还是现实“Sign in”链接。(原来都为空,我刚刚测试了拷贝成一样的,测试结果一样。),在rails console
中可以可以使用User.find_by_remember_token('i1twSVTRkMLHLk1wIX2rVA')
可以查出来(具体如下)。为啥在页面是否登陆的判断中,获取当前用户的方法却查询不出数据呢?又测试了几次,又可以正常登陆了。很诡异。
irb(main):002:0> User.find_by_remember_token('i1twSVTRkMLHLk1wIX2rVA')
User Load (1.0ms)
SELECT "users".* FROM "users" WHERE "users"."remember_token" = 'i1twSVTRkMLHLk1wIX2rVA' LIMIT 1
=> #<User id: 1, name: "Dgg", email: "lee*****@gmail.com", created_at: "2013-07-08 08:20:43", updated_at: "2013-07-08 08:20:43", password_digest: "$2a$10$.J5oTtqmFWBi4C7nxdn4huE9QvJBUBqD9/ROm/jniKJ...", remember_token: "i1twSVTRkMLHLk1wIX2rVA">
#…因为会输出一些诡异字符,这里贴的结果做了简单调整
下面再说说退出时的诡异现象。
退出时,路由设置如下:
# /config/routes.rb
resources :sessions, only: [:new, :create, :destroy]
match '/signin', to: 'sessions#new'
match '/signout', to: 'sessions#destroy', via: :delete
# …
退出的 Action 方法:
# /app/helpers/sessions_helper.rb
class SessionsController < ApplicationController
def destroy
sign_out
redirect_to root_path
end
end
退出的辅助方法sign_out
如下:
# /app/helpers/sessions_helper.rb
module SessionsHelper
def sign_out
self.current_user = nil
cookies.delete(:remember_token)
end
# …
end
退出之后,在页面 header 中判断是否登陆的代码入下:
<!--/app/views/layouts/_header.html.erb-->
<% if signed_in? %>
<li><%= link_to "Users", '#' %></li>
<li id="fat-menu" class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
Account <b class="caret"></b></a>
<!--#……-->aa
<% else %>
<li><%= link_to "Sign in", signin_path %></li>
<% end %>
判断是否登陆的辅助方法signed_in?
代码如下:
# /app/helpers/sessions_helper.rb
module SessionsHelper
def signed_in?
!current_user.nil?
end
def current_user
@current_user ||= User.find_by_remember_token(cookies[:remember_token])
end
end
疑问二:为啥在上面的退出辅助方法sign_out
中已经将self.current_user = nil
,而在判断是否登陆的辅助方法signed_in?
中却可以得出true
的结果?我刚刚又特意把remember_token
设置成空或者同样的值,试了试,却又正常了。诡异
求各位继续解答。谢谢!
在 http://railstutorial-china.org/chapter8.html 页面中,也就是教程第八章里面已经提醒了已经存在的用户需要生成 remember_token,可以搜下面的内容:
不过,如果你在浏览器中查看的话,网站还不能正常使用。这是因为“记住我”这个功能要求用户记录的记忆权标属性不为空,而现在这个用户是在 7.4.3 节中创建的,远在实现生成记忆权标的回调函数之前,所以记忆权标还没有值。为了解决这个问题,我们要再次保存用户,触发代码 8.18 中的 before_save 回调函数,生成用户的记忆权标:
在确保你代码没有写错的情况下(我看下来应该都没什么问题),对于问题 1,数据库中有数据不代表你就已经登陆了。测试signed_in?
与浏览器 cookies 有关,若是 cookies 中无数据,一样是认为未登录。问题 2,应该还是和 cookies 有关,建议每次测试前都把 run 下rake db:reset;
以确保数据可靠。我已经 clone 过楼主的代码,我本机上测试,对于疑问 1 和 2 都没有问题。
PS
楼主现在还没实现用户访问验证机制的 before_filter 吧。我看我走localhost:3000/users/1
还是可以访问,即使不登陆。