我将这些方法设置为自动加密值。
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/