Rails 在看《Ruby on Rails Tutorial》,不能登陆和退出。求解答!

diguage · 2013年07月07日 · 最后由 diguage 回复于 2013年07月08日 · 4735 次阅读

本人在看《Ruby on Rails Tutorial(第二版)》学习 Rails。看完第八章登陆和退出,自己敲入里面的代码,运行测试一切正常。

但是我在看第九章时, 点击页面上的退出按钮直接跳转到首页但却没有退出,右上角还是可以看到“Account”,有下列菜单。 我查看了退出的代码,没有发现什么问题。

另外, 直接输入登陆链接,登陆进去之后,能登陆进去,但是右上角却还是显示“Sign in”。

请帮忙看看这是怎么回事?谢谢!

另外,如何 单步调试 Rails 程序?需要什么工具?怎么整?在 RubyMine 能否可以?还请指点一下。谢谢!

代码: https://github.com/diguage/rortutorial

《Ruby on Rails Tutorial(第二版)》中文版: http://railstutorial-china.org/

不是每一步都有测试吗?我当时跟着英文版做的,做完最后一章,没有任何问题。这种手把手的教程,自己写的和教程中的多比对一下,应该能发现错在哪了

#1 楼 @apprentice 我看了,没有发现哪里有错误。实在是郁闷啊!!

测试都能通过吗?对照一下英文版代码,复制粘贴一下,是不是能用,如果可以,就是自己代码某个地方错了

#3 楼 @apprentice 测试都通过了。要不你 clone 下来,试一下?哈哈

你开发环境已经存在的用户生成了 remember_token 了吗?

一个是 session 一个是 view template 里面的 if 判断啊 应该没有别的可能出问题了吧……

@diguage

  1. 确保执行了User.all.each { |user| user.save(validate: false) }
  2. 查看浏览器的 cookie

rubymine 可以单步调试

#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 啊。

#8 楼 @leonworld 我试了试,重视失败,搞不成单步调试!很郁闷!!

#7 楼 @andor_chen 查看了浏览器中的 Cookie,remember_token竟然为空。这个何解啊?!

手机上不好查看代码,目测是 strong parameter 的问题,rails 3 的话就是 attr_accessible 的问题。

额... 搞错了,不是这问题

#13 楼 @blacktulip 谢谢!我看看啊!

  1. user.rb 模型代码没有问题;打开 console 查看`User.find_by_id(a number you want).remember_token 查看是否存在此属性。
  2. 检查所用的 helper 方法是否确实书写无误。controller 的 before_filter 的调用是否正确
  3. 检查 view 的 if 条件判定。
  4. 强制把 remember_token 和 cookie 置空,验证是否依然问题存在。

最后,楼主需要冷静分析问题,很多时候是因为粗心或者不知其原理而想当然写出的代码而导致的问题。最好能提供相关代码段,不要整篇都贴。

  1. 确保用户的remember_token有值,如果为空;
  2. rails s 进入控制台,执行User.all.each { |user| user.save(validate: false) }生成remember_token

#14 楼 @diguage 不是,搞错了... 不是这问题...

18 楼 已删除

#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还是可以访问,即使不登陆。

#21 楼 @Ryan 这部分验证在第九章中,第九章正在练习,完成后会提交到 Github 上。谢谢您的指点!哈哈

需要 登录 后方可回复, 如果你还没有账号请 注册新账号