【问题标题】:Rails 5+ API: single endpoint for both individual JSON object and array of JSON objectsRails 5+ API:单个 JSON 对象和 JSON 对象数组的单一端点
【发布时间】:2019-03-20 04:45:33
【问题描述】:

我正在使用 JSON 作为前端和后端之间的格式构建 Rails 5+ API。

我希望能够创建单个记录或多个记录,具体取决于是否发送 JSON 对象数组。

请注意,我没有使用 JSON:API 规范,而是 JSON 对象作为根属性出现。

# Create a single child object for the associated parent
POST api/v1/parents/1/children PARAMS: { child_name: "Alpha" } 

# Create multiple children objects for the associated parent
POST api/v1/parents/1/children PARAMS: [{ child_name: "Alpha" }, { child_name: "Bravo" }]

在控制器中,我必须区分发送的是单个对象还是数组。如果设置了 Content-Type="application/json" 标头,Rails 似乎会自动将 JSON 数据转换为 params["_json"] 键,我正在使用它来判断是否传递了数组。

class ChildrenController < ApplicationController
  def create
    @parent = Parent.find(params[:parent_id])

    if params["_json"] && params["_json"].is_a?(Array)
      @children = []
      params["_json"].each do |child_attributes|
        @children << @parent.children.create!(child_attributes)
      end

      render json: @children
    else
      @child = @parent.children.create!(child_params)
      render json: @child
    end
  end

  def child_params
    params.permit(:child_name)
  end
end

问题

  1. 使用params["_json"] 是判断数组是否通过的标准方法吗?这似乎很老套,但我不确定有更好的方法。
  2. 如果传递了一个JSON对象数组,我怎么还能使用StrongParameterschild_params方法呢?目前,如果用户传递一个 JSON 对象数组,他们可以放置他们想要的任何属性,而我不会将它们过滤掉。
  3. 是否有更好的方法来实现此功能?我不必为单个和多个创建都使用单个端点,我只是认为拥有一个可以处理单个或多个对象的 API 端点会更方便。
  4. 我还计划为update 操作创建一个端点,该端点也可以接受单个或多个对象。这是一种不好的做法吗?

【问题讨论】:

    标签: arrays json ruby-on-rails-5 rails-api


    【解决方案1】:

    如果您将 JSON 数组(如 [{ child_name: "Alpha" }, { child_name: "Bravo" }])发送到端点,Rails 会将其存储到 params 哈希的 _json 中。这样做的原因是,与单个 JSON 对象不同,如果不引入另一个键,则无法将数组提取到哈希中。

    假设我们将{ child_name: "Alpha" } 发布到端点。参数将如下所示:

    {"child_name"=>"Alpha", "format"=>:json, "controller"=>"api/children", "action"=>"create"}
    

    现在,如果我们发布数组 [{ child_name: "Alpha" }, { child_name: "Bravo" }],它将像 JSON 对象一样盲目提取到哈希中,结果将如下所示:

    {[{ child_name: "Alpha" }, { child_name: "Bravo" }], "format"=>:json, "controller"=>"api/children", "action"=>"create"}
    

    这不是有效的哈希值!所以 Rails 将你的 JSON 数组包装到 _json 中,它变成:

    {"_json" => [{ child_name: "Alpha" }, { child_name: "Bravo" }], "format"=>:json, "controller"=>"api/children", "action"=>"create"}
    

    这发生在here in the actionpack。是的,这似乎有点 hacky,但唯一可能更干净的解决方案是将请求正文中的所有数据包装到一个键中,例如 data。

    所以对于您的实际问题:如何在单个端点管理 JSON 对象和 JSON 数组?

    您可以使用强参数,例如 ..

    def children_params
      model_attributes = [:child_name]
      if params.key? '_json'
        params.permit(_json: model_attributes)['_json'] 
      else
        params.permit(*model_attributes)
      end
    end
    

    .. 在创建动作中看起来像 ..

    def create
      result = Children.create!(children_params)
      if children_params.kind_of? Array
        @children = result
        render :index, status: :created
      else
        @child = result
        render :show, status: :created
      end
    end
    

    当然,您需要根据您的特定用例对其进行一些调整。从您的 API 的设计角度来看,我认为这样做是可以的。也许这应该在一个单独的问题中提出。

    【讨论】:

      猜你喜欢
      • 2021-07-31
      • 2017-10-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-01
      • 2017-12-05
      • 2021-09-22
      • 1970-01-01
      相关资源
      最近更新 更多