[英]How to transform nested parameters in Rails API for PATCH requests
I'm having problems trying to implement a PATCH endpoint for a Rails API which deals with complex request objects that are structurally different from the ActiveRecord
model.我在尝试为 Rails API 实现 PATCH 端点时遇到问题,该端点处理在结构上不同于ActiveRecord
model 的复杂请求对象。
As an example let's say I have the following request object:例如,假设我有以下请求 object:
{
"details": {
"color": {
"id": 1
}
},
"name": "Hello, world!"
...
}
However, on my model I expect a flat color_id
attribute:但是,在我的 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
Therefore I need to transform the request params.因此我需要转换请求参数。 For this I've found one approach which works pretty well in case of PUT requests, but not at all for PATCH:为此,我找到了一种在 PUT 请求的情况下效果很好的方法,但对于 PATCH 则完全没有:
ActionController::Parameters.new({
color_id: params.dig(:details, :color, :id),
name: params.dig(:name)
})
If I issue a PUT request this solution works great since PUT expects the whole object as payload, PATCH on the other hand would cause issues when passing only a subset of the properties since everything else will be set to nil
due to how dig
works.如果我发出 PUT 请求,此解决方案效果很好,因为 PUT 期望整个 object 作为有效负载,另一方面,当仅传递属性的子集时,PATCH 会导致问题,因为由于dig
的工作方式,其他所有内容都将设置为nil
。
Assuming I have no control over the request format, how can I transform the request params in the backend so that omitted keys will not result in nil
values?假设我无法控制请求格式,如何在后端转换请求参数,以便省略的键不会导致nil
值? Of course I could imperatively handle each property line by line, checking whether the key is present in the original params and then setting it in the new one, but is there a more elegant approach?当然,我可以命令式地逐行处理每个属性,检查密钥是否存在于原始参数中,然后将其设置为新参数,但有没有更优雅的方法?
I've found a generic solution using mapping logic with a lookup table.我找到了一个使用带有查找表的映射逻辑的通用解决方案。 For the example above:对于上面的例子:
{
"details": {
"color": {
"id": 1
}
},
"name": "Hello, world!"
...
}
I would have the following mapping variable:我将有以下映射变量:
MAPPING = {
[:details, :color, :id] => [:color_id]
}
Then I'm able to transform the params using this recursive algorithm:然后我可以使用这个递归算法来转换参数:
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.