使用 Jbuilder(或其他)的 Rails JSON API 布局
Posted
技术标签:
【中文标题】使用 Jbuilder(或其他)的 Rails JSON API 布局【英文标题】:Rails JSON API layouts with Jbuilder (or other) 【发布时间】:2012-07-16 00:39:09 【问题描述】:在我的 rails 3.2 应用程序中,我使用 jbuilder 来呈现来自我的 JSON api 的响应。
我想为所有 API 响应提供一个通用结构,而布局可能是让我的视图保持干燥的解决方案。
例如:我希望每个回复都采用以下形式:
status: "ok|error|redirect",
data: ... JSON specific to the current view ... ,
errors: [ ... ],
notes: [ ... ]
(其中 data 的值是视图提供的 json 结构,其他一切都来自布局)
但是:我无法让 jbuilder 布局正确地屈服于视图。
# in layout
json.data yield
# in view
json.some "value"
结果:
"data":"\"some\":\"value\"" # arg! my json has become a string
尝试另一种方式:
# in layout
yield
# in view
json.data do |json|
json.some "value"
end
结果:
有没有人用 jbuilder 或其他 json 模板 gem/方法成功地做到这一点?
这个juilder github issue 表明这是可能的,但表明其他人也有类似的问题。
我看到 rabl (https://github.com/nesquena/rabl/) 应该支持布局 (https://github.com/nesquena/rabl/wiki/Using-Layouts),但我已经决定出于其他原因不要使用它(rabl 使复杂的 json 结构成为一场噩梦,尤其是在尝试控制对象根等时)。
【问题讨论】:
【参考方案1】:我会根据我们提出的解决方案为您提供替代方案:
# app/helpers/application_helper.rb
module ApplicationHelper
def envelope(json, status, errors, notes)
json.status status
json.data do
yield if block_given?
end
json.errors errors
json.notes notes
end
end
然后,在视图中,您可以调用信封并包含您的 json 代码,例如:
# app/views/api/v1/test/show.json.jbuilder
envelope(json, "OK" ) do
json.some 'value'
end
【讨论】:
在视图代码中envelope(json, "OK" )
后面缺少do
关于***.com/questions/18202750/…的任何想法【参考方案2】:
你可以这样做
# api.v1.json.jbuilder - layout
json.request do
json.message "your message"
json.status 200
end
json.data JSON.parse(yield)
# show.json.jbuilder - action view
json.name 'Some item name'
【讨论】:
我喜欢你用这个去的地方,但是解析 JSON 只是把它直接吐出来似乎有点浪费 Peter:我一直在这样做,而且它有效,但它是一个巨大的组合。 JSON.parse 在“智能”转换数据类型时会导致某些视图出现问题。例如,一些字符串正在变成数字。【参考方案3】:迟到的答案,但帮助我得到了我正在寻找的东西......
成功结果:
"something": "id": 42, "message": "hello", "status": "ok", "errors": []
错误结果:
"something": null, "status": "error", "errors": ["could not do the thing"]
代码:
app/controllers/api/v1/base_controller.rb
class Api::V1::BaseController < ActionController::API
layout 'api/v1/application'
before_action :setup_layout_elements
def setup_layout_elements
@status = :ok
@errors = []
end
def error!(message)
@status = :error
@errors << message
nil
end
end
app/controllers/api/v1/some_controller.rb
class Api::V1::SomeController < Api::V1::BaseController
def index
@something = begin
possibly_error_causing_code
rescue
error!('could not do the thing')
end
render builder: 'api/v1/something/index'
end
end
app/views/layouts/api/v1/application.json.jbuilder
json.merge! JSON.parse(yield)
json.status @status
json.errors @errors
app/views/api/v1/something/index.json.jbuilder
json.something do
json.id @something.id
json.message @something.to_s
end
【讨论】:
很好的解决方案,正是我想要的。谢谢。【参考方案4】:试试
json.merge! JSON.parse(yield)
https://github.com/rails/jbuilder/issues/8#issuecomment-27586784
【讨论】:
【参考方案5】:JBuilder 不支持使用 json.jbuilder
作为您的布局(请参阅 Github 上的问题 #172)。
通过使用json.erb
作为我的布局格式,我设法避免了额外的一轮parse
&generate
。
app/controllers/api/base_controller.rb:
class Api::BaseController < ActionController::Base
layout "api.v1"
end
app/views/layouts/api.v1.json.erb:
<% if @api_errors.present? %>
"errors": <%= raw JSON.dump @api_errors %>,
<% else %>
"data": <%= yield %>,
<% end %>
"meta": <%= raw JSON.dump @api_meta %>
【讨论】:
【参考方案6】:如果您不想包含额外的密钥,您可以这样做
class UsersController < ApplicationController
layout: 'json_layout'
end
在 /app/views/layouts/json_layout.json.jbuilder 中
json.success true
r = JSON.parse(yield)
r.each|k,v|
json.set! k,v
【讨论】:
【参考方案7】:jbuilder 是一种非常简单的 API 视图技术,您可以在此处添加部分响应,因此如果您希望所有 API 的响应相同,请创建装饰器或为公共响应创建部分响应,并在需要时调用该响应
如果你愿意,可以说
status: "ok|error|redirect",
data: ... JSON specific to the current view ... ,
errors: [ ... ],
notes: [ ... ]
为此创建一个部分 /views/api/common/_some_partial
json.status "ok whatever the message"
json.data do
json.message "message"
end
json.errors @errors
json.notes @notes_array
它对您的问题非常简单的解决方案。
干杯
【讨论】:
以上是关于使用 Jbuilder(或其他)的 Rails JSON API 布局的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Jbuilder 中使用带有 Gon gem 的 Rails 助手?
如何使用 Jbuilder & Rails 生成自定义 json 响应
Rails jbuilder DateTime将小数添加到秒
在 Rails 4 中用于 JSON 处理的 jbuilder vs rails-api/active_model_serializers