ruby-on-rails - `form_for` 绕过模型访问器。如何让它停止? (或 : How to make a custom attribute serializer? )

标签 ruby-on-rails activerecord ruby-on-rails-5

我将这些方法设置为自动加密值。

class User < ApplicationRecord
  def name=(val)
    super val.encrypt
  end
  def name
    (super() || '').decrypt
  end

当我尝试提交表单时出现错误(缺少电话),然后 name属性显示为乱码。
<input class="form-control" type="text" value="Mg8IS1LB2A1efAeZJxIDJMSroKcq6WueyY4ZiUX+hfI=" name="user[name]" id="user_name">

它在验证成功时起作用。当我逐行浏览 Controller 时,它也可以在控制台中工作 #update .
irb(main):015:0> u = User.find 1
irb(main):016:0> u.name
=> "Sue D. Nym"
irb(main):017:0> u.phone
=> "212-555-1234"
irb(main):018:0> u.update name: 'Sue D. Nym', phone: ''
   (10.0ms)  BEGIN
   (1.0ms)  ROLLBACK
=> false
irb(main):020:0> u.save
=> false
irb(main):029:0> u.errors.full_messages.join ','
=> "Phone can't be blank"
irb(main):031:0> u.build_image unless u.image
=> nil
irb(main):033:0> u.name
=> "Sue D. Nym"
users_controller.rb
  def update
    @user = User.find current_user.id
    @user.update user_params
    if @user.save
      flash.notice = "Profile Saved"
      redirect_to :dashboard
    else
      flash.now.alert = @user.errors.full_messages.join ', '
      @user.build_image unless @user.image
      render :edit
    end
  end

该 View 以某种方式获取加密值而无需通过 #name ,并且仅在验证失败之后。

我将 Controller 降低到绝对最小值,但它在 #update 后立即失效。 .但是,它可以在控制台上运行!
  def update
    @user = User.find current_user.id
    @user.update user_params
    render :edit
    return

我将 View 缩小到绝对最小值,它显示了名称,但仅在 form_for 之外。 .我还不知道为什么。
edit.haml
=@user.name
=form_for @user, html: { multipart: true } do |f|
  =f.text_field :name
HTML source
<span>Sue D. Nym</span>
<form class="edit_user" id="edit_user_1" enctype="multipart/form-data" action="/users/1" accept-charset="UTF-8" method="post">
  <input name="utf8" type="hidden" value="✓"><input type="hidden" name="_method" value="patch"><input type="hidden" name="authenticity_token" value="C/ScTxfENNxCKgzG0qAlPElOKI7nOYxZimQ7BsB64wIWQ9El4+vOAfxX3qHL08rbr0sxRiJnzQti13e4DAgkfQ==">  
  <input type="text" value="sER9cjwa6Ov5weXjEQN2KJYoTOXtVBytpX/cI/aPrFs=" name="user[name]" id="user_name">
</form>

我注意到 attributes仍然返回加密值,所以我尝试添加它,但 form_for仍然设法获取加密值并将其放入表单中!
  def attributes
    attr_hash = super()
    attr_hash["name"] = name
    attr_hash
  end

rails 5.0.2

最佳答案

虽然你可以通过重载 name_before_type_case 来解决这个问题。 ,我认为这实际上是进行这种转换的错误地方。

根据您的示例,此处的要求似乎是:

  • 内存中的明文
  • 静态加密

  • 因此,如果我们将加密/解密转换移动到 Ruby-DB 边界,这个逻辑就会变得更加清晰和可重用。

    Rails 5 引入了一个有用的 Attributes API用于处理这种确切的情况。由于您没有提供有关如何实现加密例程的详细信息,我将使用 Base64在我的示例代码中演示文本转换。
    app/types/encrypted_type.rb
    class EncryptedType < ActiveRecord::Type::Text
      # this is called when saving to the DB
      def serialize(value)
        Base64.encode64(value) unless value.nil?
      end
    
      # called when loading from DB
      def deserialize(value)
        Base64.decode64(value) unless value.nil?
      end
    
      # add this if the field is not idempotent
      def changed_in_place?(raw_old_value, new_value)
        deserialize(raw_old_value) != new_value
      end
    end
    
    config/initalizers/types.rb
    ActiveRecord::Type.register(:encrypted, EncryptedType)
    

    现在,您可以将此属性指定为模型中的加密:
    class User < ApplicationRecord
      attribute :name, :encrypted
    
      # If you have a lot of fields, you can use metaprogramming:
      %i[name phone address1 address2 ssn].each do |field_name|
        attribute field_name, :encrypted
      end
    end
    
    name属性将在到数据库的往返过程中被透明地加密和解密。这也意味着您可以将相同的转换应用于任意数量的属性,而无需重写相同的代码。

    关于ruby-on-rails - `form_for` 绕过模型访问器。如何让它停止? (或 : How to make a custom attribute serializer? ),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44509420/

    相关文章:

    ruby-on-rails - 如何在两次之间进行搜索?

    ruby-on-rails - button =作为elasticsearch搜索表单的最后一个参数附加

    mysql - rails : how to select_rows with fields names (mysql)

    nhibernate - NHibernate session 究竟是什么?

    ruby-on-rails - 错误 : Exhausted all sources trying to fetch version 'latest' of RVM

    ruby-on-rails - Rails/Ajax/错误处理

    ruby-on-rails - Rspec - stub 模块方法

    ruby-on-rails - 包含和引用另一个表时无法解析的列的自定义名称

    ruby-on-rails - rails : Mapping a Date from form parameters into ActiveModel object

    ruby-on-rails - 无法区分 Rails 管理中的新建操作和编辑操作