Подтвердить что ты не робот

Рельсы 3 формат ответа и управление версиями с использованием типа MIME поставщика в заголовке Accept

Преамбула:

Я исследовал версию API и нашел несколько способов сделать это. Я решил попробовать предложение peter williams и создал новые типы mime поставщика, чтобы указать версию и формат. Я не мог найти окончательной записи для этого, следуя "пути рельсов", поэтому я собрал информацию из нескольких мест. Я смог заставить его работать, но есть некоторая глупость в том, как рендереры обрабатывают массив Widget vs Widget в respond_with.

Основные шаги и проблемы:

Я зарегистрировал типы mime и добавил рендереры для версии 1 как в xml, так и в json для ApplicationController, методы рендеринга call to_myproj_v1_xml и to_myproj_v1_json в модели. respond_with(@widget) работает отлично, но respond_with(@widgets) выбрасывает HTTP/1.1 500 Internal Server Error, говоря, что "Шаблон отсутствует".

Временное решение:

"Шаблон отсутствует" означает, что рендер не был вызван и не существует подходящего шаблона. случайно, я обнаружил, что он ищет метод класса... поэтому я придумал код, ниже которого работает, но я не очень доволен им. Глупость в основном связана и связана с xml = obj.to_myproj_v1_xml(obj) и дублированием в модели.

Мой вопрос: кто-нибудь сделал что-то подобное в несколько более чистом виде?

- = обновленный код = -

конфиг/Инициализаторы/mime_types.rb

Mime::Type.register 'application/vnd.com.mydomain.myproj-v1+xml', :myproj_v1_xml
Mime::Type.register 'application/vnd.com.mydomain.myproj-v1+json', :myproj_v1_json

приложение/контроллеры/application_controller.rb

class ApplicationController < ActionController::Base
  protect_from_forgery
  before_filter :authenticate

  ActionController.add_renderer :myproj_v1_xml do |obj, options|
    xml = obj.to_myproj_v1_xml
    self.content_type ||= Mime::Type.lookup('application/vnd.com.mydomain.myproj-v1+xml')
    self.response_body = xml
  end

  ActionController.add_renderer :myproj_v1_json do |obj, options|
    json = obj.to_myproj_v1_json
    self.content_type ||= Mime::Type.lookup('application/vnd.com.mydomain.myproj-v1+json')
    self.response_body  = json
  end
end

приложение/модели/widget.rb

class Widget < ActiveRecord::Base
  belongs_to :user
  V1_FIELDS = [:version, :model, :description, :name, :id]

  def to_myproj_v1_xml
    self.to_xml(:only => V1_FIELDS)
  end

  def to_myproj_v1_json
    self.to_json(:only => V1_FIELDS)
  end

  def as_myproj_v1_json
    self.as_json(:only => V1_FIELDS)
  end
end

приложение/контроллеры/widgets_controller.rb

class WidgetsController < ApplicationController

  respond_to :myproj_v1_xml, :myproj_v1_json

  def index
    @widgets = @user.widgets
    respond_with(@widgets)
  end

  def create
    @widget = @user.widgets.create(params[:widget])
    respond_with(@widget)
  end

  def destroy
    @widget = @user.widgets.find(params[:id])
    respond_with(@widget.destroy)
  end

  def show
    respond_with(@widget = @user.widgets.find(params[:id]))
  end

...

end

конфигурации/инициализаторы/monkey_array.rb

class Array

  def to_myproj_v1_json(options = {})
    a = []
    self.each { |obj| a.push obj.as_myproj_v1_json }
    a.to_json()
  end

  def to_myproj_v1_xml(options = {})
    a = []
    self.each { |obj| a.push obj.as_myproj_v1_json } # yes this is json instead of xml.  as_json returns a hash
    a.to_xml()
  end

end

UPDATE:

Нашел еще одно решение, которое чувствует себя лучше, но все еще немного странно (мне все еще не совсем удобно с патчами обезьян), возможно, нормально, хотя... в основном перемещало построение данных ответа из метода класса to_myproj_v1_json на патч обезьяны на массиве. Таким образом, когда есть массив виджетов, он вызывает метод экземпляра as_myproj_v1_json для каждого виджета и возвращает весь массив в нужном формате.

Одно примечание:

  • as_json не имеет ничего общего с json-форматом, просто создает хэш. Добавьте настраиваемое форматирование в as_myproj_v1_json (или переопределение as_json, если вы не используете пользовательские типы mime), тогда to_json изменит хэш на строку json.

Я обновил код ниже, чтобы быть тем, что в настоящее время используется, поэтому исходный вопрос может не иметь смысла. если кто-то захочет исходный вопрос и код, показанный как есть, и фиксированный код в ответе, я могу это сделать.

4b9b3361

Ответ 1

Для ответа: см. вопрос: -)

Короче говоря, существуют разные решения, из которых один находится в вопросе выше:

  • Monkey-patch Array для реализации метода, который даст (старый) v1 JSON обратно

Ответ 2

Я не видел этого трюка типа контента, используемого где-либо в проекте Rails, прежде чем это стало новым для меня. Как я обычно видел, это определение пространства имен маршрутов (например,/api/v1/), которое идет на контроллер (скажем, Api:: Version1Controller).

Кроме того, я знаю, что вы хотите делать вещи "Rails way", и, может быть, это звучит как crotchety, исходящий от парня, который был с Rails с версии 1.3, но весь материал respond_with/respond_to довольно мал меня. Я не знал, что respond_to ищет метод to_XXX, когда он сериализует объекты, например (возможно, мне нужно прочитать об этом). Имеющеся в такой же манере, как кажется, довольно глупо. Кроме того, для API форматирование данных модели - это действительно работа вида, а не модель. В этом случае я мог бы рассмотреть что-то вроде rabl. Там хорошая запись об этом здесь.