繁体   English   中英

如何在 Rails API 中为 PATCH 请求转换嵌套参数

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM