【问题标题】:Rails JSON API layouts with Jbuilder (or other)使用 Jbuilder(或其他)的 Rails JSON API 布局
【发布时间】: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 结构成为一场噩梦,尤其是在尝试控制对象根等时)。

【问题讨论】:

    标签: ruby-on-rails json layout jbuilder


    【解决方案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
    

    【讨论】:

    【解决方案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】:

    【讨论】:

      【解决方案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
          

          它对您的问题非常简单的解决方案。

          干杯

          【讨论】:

            猜你喜欢
            • 2014-08-17
            • 2014-11-15
            • 1970-01-01
            • 2014-11-23
            • 2012-10-03
            • 1970-01-01
            • 2012-10-03
            • 2014-08-09
            • 1970-01-01
            相关资源
            最近更新 更多