使用Jbuilder(或其他)Rails JSON API布局

在我的rails 3.2应用程序中,我使用jbuilder来呈现来自我的JSON api的响应。

我想为所有API响应提供一个通用结构,并且布局可能是保持我的视图DRY的可能解决方案。

例如:我希望每个回复都是以下forms:

{ status: "ok|error|redirect", data: { ... JSON specific to the current view ... }, errors: [ ... ], notes: [ ... ] } 

(其中数据的值是视图提供的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问题表明它是可能的,但表明其他人也有类似的问题。

我看到rabl(https://github.com/nesquena/rabl/)应该支持布局(https://github.com/nesquena/rabl/wiki/Using-Layouts),但我决定不使用由于其他原因(rabl使复杂的json结构成为一场噩梦,特别是在试图控制对象根等时)。

你可以通过这种方式做到这一点

 # 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' 

我会根据我们提出的解决方案为您提供替代方案:

 # 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 

迟到的答案,但帮助我得到了我想要的…

成功结果:

 { "something": {"id": 42, "message": "hello"}, "status": "ok", "errors": [] } 

错误结果:

 { "something": null, "status": "error", "errors": ["could not do the thing"] } 

码:

应用程序/控制器/ 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 

应用程序/控制器/ 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 

应用程序/视图/布局/ API / V1 / application.json.jbuilder

 json.merge! JSON.parse(yield) json.status @status json.errors @errors 

应用程序/视图/ API / V1 /事/ index.json.jbuilder

 json.something do json.id @something.id json.message @something.to_s end 

如果您不想包含额外的密钥,您可以这样做

 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 } 

JBuilder不支持使用json.jbuilder作为您的布局(请参阅Github上的问题#172 )。

我设法避免使用json.erb作为我的布局格式进行额外的parsegenerate

应用程序/控制器/ API / base_controller.rb:

 class Api::BaseController < ActionController::Base layout "api.v1" end 

应用程序/视图/布局/ api.v1.json.erb:

 { <% if @api_errors.present? %> "errors": <%= raw JSON.dump @api_errors %>, <% else %> "data": <%= yield %>, <% end %> "meta": <%= raw JSON.dump @api_meta %> } 

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 

它为您的问题提供了非常简单的解决方案

干杯