Rails 5+ API:单个JSON对象和JSON对象数组的单个端点

问题描述 投票:3回答:1

我正在使用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" }]

在控制器中,我必须区分是发送单个对象还是数组。如果设置了params["_json"]标头,Rails似乎会自动将JSON数据转换为Content-Type="application/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对象数组,如何仍然使用StrongParameters child_params方法?当前,如果用户传递一个JSON对象数组,他们可以放置所需的任何属性,而我不会将其过滤掉。
  3. 是否有更好的方法来实现此功能?我不必为单个创建和多个创建都使用单个端点,我只是想拥有一个可以处理单个或多个对象的API端点会更方便。
  4. 我还计划为update操作创建单个端点,该端点也可以接受单个或多个对象。这是不好的做法吗?
arrays json ruby-on-rails-5 rails-api
1个回答
0
投票

如果您将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对象那样被[[blindly提取到哈希中,结果将如下所示:

{[{ 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。是的,这似乎有点hacker,但是唯一且可能更简洁的解决方案是将请求主体中的所有数据包装到一个键中,例如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

..并且在create action中看起来像..

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的设计角度来看,我认为这样做是可以的。也许应该在一个单独的问题中提出。
© www.soinside.com 2019 - 2024. All rights reserved.