【问题标题】:A missing attribute when using { render json: }使用 { render json: } 时缺少属性
【发布时间】:2012-04-22 04:54:15
【问题描述】:

我有一个使用 devise 登录的应用程序。我在一个 Web 应用程序中有一个登录表单,它对数据库的用户进行身份验证,并简单地将 @user 哈希作为 json 字符串返回。

目标是让用户对用户进行身份验证并检索他们的 authentication_token 以供将来在应用程序中使用,以防止用户必须不断登录。

问题是我无法将 authentication_token 包含在返回的 json 中。

我的 user.rb 模型

attr_accessible :authentication_token, :email, :password, :password_confirmation, :remember_me,  :bio, :confirmed, :deposit, :email, :fri25, :mon21, :name, :paid, :picture, :sat26, :sun20, :sun27, :thur24, :tue22, :wed23

明确包含 authentication_token 符号。

然后在会话控制器中,我有一个名为 newApi 的自定义操作,它运行一个简单的身份验证方法并以 json 形式的 @user 哈希响应。

def newapi
 @user = User.authenticate(params[:email],params[:password])
 respond_to do |format|
   format.json { render json: @user }
 end

结束

然而,无论我做什么,身份验证令牌都不包含在 json 响应中。我错过了什么明显的东西吗?为什么不包括在内?

【问题讨论】:

    标签: ruby-on-rails json devise render


    【解决方案1】:

    Devise > 2.x 似乎从 xml 和 json 响应中去除了属性。这可能是一项安全功能,但我不确定它是从哪里引入的。 “受保护的属性”列表在这里:

    https://github.com/plataformatec/devise/blob/master/lib/devise/models/authenticatable.rb

    第 54 行:

    BLACKLIST_FOR_SERIALIZATION = [:encrypted_password, :reset_password_token, :reset_password_sent_at,
        :remember_created_at, :sign_in_count, :current_sign_in_at, :last_sign_in_at, :current_sign_in_ip,
        :last_sign_in_ip, :password_salt, :confirmation_token, :confirmed_at, :confirmation_sent_at,
        :remember_token, :unconfirmed_email, :failed_attempts, :unlock_token, :locked_at, :authentication_token]
    

    初始化它的代码在第 122-135 行

    cmets 说您可以提供自己的黑名单属性列表,或分别使用:force_except:except 附加到现有列表。

    我的 hacky 解决方案是这样的:

    def newapi
      @user = User.authenticate(params[:email],params[:password])
      respond_to do |format|
        format.json { render json: @user.as_json(:force_except => Devise::Models::Authenticatable::BLACKLIST_FOR_SERIALIZATION.delete_if{|x| x == :authentication_token} }
      end
    end
    

    但我认为有一种方法可以更优雅地覆盖此列表,例如在初始化程序中。我的工作,但可能不是最好的答案。

    【讨论】:

      【解决方案2】:

      虽然这很旧,但我所做的只是使用它的“方法”选项

      to_json(methods: :arbitrary_method)
      

      因为所有属性在技术上都成为模型上的可访问方法。这似乎最轻松:D

      在这个页面上:http://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html

      【讨论】:

        【解决方案3】:

        我的解决方法:

        class User < ActiveRecord::Base
          # include devise token_authenticatable and others
          devise  :token_authenticatable
        
          # after devise finds our user (such as when logging in)
          after_find :write_auth_token
        
          # write our pseudo attribute to the model
          def write_auth_token
            write_attribute(:auth_token, self.authentication_token)
          end
        
          # make our pseudo attribute attr_accessible
          attr_accessible :auth_token
        end
        

        【讨论】:

          【解决方案4】:

          我最终通过使用jbuilder 自己解决了这个问题。

          基本上您只需删除 render json: @user 调用并使用 jbuilder 模板。

          在 newapi.json.jbuilder 中

          json.(
            @user, 
            :authentication_token, 
            :email, 
            :password, 
            :password_confirmation, 
            :remember_me,
            :bio, 
            :confirmed, 
            :deposit, 
            :email, 
            :fri25, 
            :mon21, 
            :name, 
            :paid, 
            :picture, 
            :sat26, 
            :sun20, 
            :sun27, 
            :thur24, 
            :tue22, 
            :wed23
          )
          

          如果你有这条路线,请查看他们的文档,因为有很多它可以满足我的所有需求,而且是我们现在做 api 返回的唯一方法。如果您之前没有使用过它,请检查一下!它使 API 端点非常简单。

          如果您想推出自己的设置,Nick Knudson 的回答也是有效的。

          【讨论】:

          • 解决方案在哪里?您已经“回答”了自己的问题(没有提供解决方案)标记为正确的问题,甚至没有投票支持导致您找到解决方案的答案。这真的不是使用 Stackoverflow 的方式。
          • 更新答案以提供示例。
          【解决方案5】:

          @Travis Todd 的回答完全可以,是的,可能会有更优雅的解决方案。此外,如果您想为需要 2 个或更多这些黑名单属性的数据库迁移或其他类似目的构建私有或临时 api,您可能需要执行以下操作:

          def newapi
            @devise_attrs = [:encrypted_password, :remember_created_at, :sign_in_count, :current_sign_in_at] #etc..
            @user = User.authenticate(params[:email],params[:password]).includes(:identity)
            respond_to do |format|
              # I added a few other options and updated ruby syntax so that you can see how can this be customized if in need of specific user attributes or relations.
              format.json { render json: @user.as_json(force_except: Devise::Models::Authenticatable::BLACKLIST_FOR_SERIALIZATION.delete_if{|x| @devise_attrs.include?(x)}, include: [:identity]), status: 200 }
            end
          end
          

          仍在等待另一个优雅的解决方案。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-11-11
            • 1970-01-01
            • 2021-06-11
            • 2021-03-10
            • 1970-01-01
            • 1970-01-01
            • 2017-05-05
            • 2020-09-19
            相关资源
            最近更新 更多