我正在构建一个使用 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" }]
在 Controller 中,我必须区分发送的是单个对象还是数组。如果设置了 Content-Type="application/json"
header ,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
问题
- 使用
params["_json"]
是否是判断数组是否已传递的标准方法?这看起来很老套,但我不确定是否有更好的方法。 - 如果传递了一个 JSON 对象数组,我如何仍然使用 StrongParameters
child_params
方法?目前,如果用户传递一组 JSON 对象,他们可以放置他们想要的任何属性,我不会过滤掉它们。 - 是否有更好的方法来实现此功能?我不必为单个和多个创建都使用单个端点,我只是认为拥有一个可以处理单个或多个对象的 API 端点会更方便。
- 我还计划为
update
操作创建一个端点,该端点也可以接受单个或多个对象。这是一种不好的做法吗?
最佳答案
如果您将 JSON 数组(如 [{ child_name: "Alpha"}, { child_name: "Bravo"}]
)发送到端点,Rails 会将其存储到 _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 的设计角度来看,我认为这样做是可以的。也许这应该在一个单独的问题中提出。
关于arrays - 轨道 5+ API : single endpoint for both individual JSON object and array of JSON objects,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52822103/