Макеты Rails JSON API с Jbuilder (или другим)

В моем приложении 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?

Эта проблема juilder github предполагает, что это возможно, но указывает на то, что у других возникают аналогичные проблемы.

Я вижу, что rabl (https://github.com/nesquena/rabl/) должен поддерживать макеты (https://github.com/nesquena/rabl/wiki/Using-Layouts), но я решил не использовать это по другим причинам (rabl делает сложные структуры json кошмаром, особенно при попытке контролировать корни объектов и т. д.).


person MikeL    schedule 17.07.2012    source источник


Ответы (7)


Вы можете сделать это таким образом

# 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'
person Dobromir Minchev    schedule 24.09.2012
comment
Мне нравится, куда вы идете с этим, но кажется несколько расточительным анализировать JSON только для того, чтобы снова выплюнуть его обратно - person Peter Nixey; 09.06.2014
comment
Питер: Я делал это таким образом, и это работает, но это огромный кладж. И JSON.parse вызывает проблемы с некоторыми представлениями, когда он интеллектуально преобразует типы данных. например, некоторые строки превращаются в числа. - person Glenn; 23.10.2018

Я дам вам альтернативу, основанную на решении, которое мы придумали:

# 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
person sorens    schedule 11.10.2012
comment
У вас отсутствует do после envelope(json, "OK" ) в коде просмотра - person JVK; 13.08.2013
comment
любая идея о stackoverflow.com/questions/18202750/ - person JVK; 13.08.2013

Поздний ответ, но помог мне получить то, что я искал...

Результат успеха:

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

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
person Chris Cashwell    schedule 15.10.2014
comment
Отличное решение, именно то, что я искал. Спасибо. - person Michael Kalygin; 04.12.2015

Пытаться

 json.merge! JSON.parse(yield)

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

person Ameen    schedule 11.08.2014

JBuilder не поддерживает использование json.jbuilder в качестве макета (см. выпуск #172 на Github).

Мне удалось избежать лишнего цикла parse&generate, используя json.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 %>
}
person Adam Wróbel    schedule 27.05.2016

Если вы не хотите включать дополнительный ключ, вы можете сделать это

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
}
person Hrishabh Gupta    schedule 06.05.2014

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

Это довольно простое решение для вашего вопроса.

Ваше здоровье

person Ali Hassan Mirza    schedule 18.09.2015