10

在我的 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 问题表明这是可能的,但表明其他人也有类似的问题。

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

4

7 回答 7

14

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

# 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
于 2012-10-11T04:14:53.673 回答
11

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

# 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'
于 2012-09-24T12:25:44.890 回答
9

迟到的答案,但帮助我得到了我想要的东西......

成功结果:

{ "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

app/views/api/v1/something/index.json.jbuilder

json.something do
  json.id      @something.id
  json.message @something.to_s
end
于 2014-10-15T16:13:46.983 回答
2

尝试

 json.merge! JSON.parse(yield)

https://github.com/rails/jbuilder/issues/8#issuecomment-27586784

于 2014-08-11T08:42:12.097 回答
2

JBuilder 不支持json.jbuilder用作您的布局(参见 Github 上的 issue #172)。

我设法通过使用parse&作为我的布局格式来避免做额外的一轮。generatejson.erb

应用程序/控制器/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 %>
}
于 2016-05-27T14:06:29.667 回答
1

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

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
}
于 2014-05-06T13:29:26.427 回答
-1

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

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

干杯

于 2015-09-18T16:09:42.553 回答