[英]How to transform nested parameters in Rails API for PATCH requests
我在尝试为 Rails API 实现 PATCH 端点时遇到问题,该端点处理在结构上不同于ActiveRecord
model 的复杂请求对象。
例如,假设我有以下请求 object:
{
"details": {
"color": {
"id": 1
}
},
"name": "Hello, world!"
...
}
但是,在我的 model 上,我希望有一个平坦的color_id
属性:
class CreateModel < ActiveRecord::Migration[7.0]
def change
create_table :model do |t|
t.string :name, null: false
t.integer :color_id, null: false
end
end
end
因此我需要转换请求参数。 为此,我找到了一种在 PUT 请求的情况下效果很好的方法,但对于 PATCH 则完全没有:
ActionController::Parameters.new({
color_id: params.dig(:details, :color, :id),
name: params.dig(:name)
})
如果我发出 PUT 请求,此解决方案效果很好,因为 PUT 期望整个 object 作为有效负载,另一方面,当仅传递属性的子集时,PATCH 会导致问题,因为由于dig
的工作方式,其他所有内容都将设置为nil
。
假设我无法控制请求格式,如何在后端转换请求参数,以便省略的键不会导致nil
值? 当然,我可以命令式地逐行处理每个属性,检查密钥是否存在于原始参数中,然后将其设置为新参数,但有没有更优雅的方法?
我找到了一个使用带有查找表的映射逻辑的通用解决方案。 对于上面的例子:
{
"details": {
"color": {
"id": 1
}
},
"name": "Hello, world!"
...
}
我将有以下映射变量:
MAPPING = {
[:details, :color, :id] => [:color_id]
}
然后我可以使用这个递归算法来转换参数:
def handle(params, keys)
output = Hash.new
params.each do |k,v|
sym_keys = (keys + [k]).map &:to_sym
target_keys = MAPPING[sym_keys]
if v.is_a?(Hash)
keys << k
tmp = handle(v, keys)
output = output.deep_merge!(tmp)
end
unless target_keys.nil?
target_value = target_keys.reverse().reduce(v) do |v, k|
Hash[k, v]
end
output = output.deep_merge!(target_value)
end
end
output
end
def transform(params)
output = handle(params, [])
end
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.