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

Преамбула:

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

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

Я зарегистрировал mime-типы и добавил рендереры для версии 1 как в xml, так и в json в ApplicationController, рендереры вызывают методы 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) и дублированием в модели.

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

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

config/initializers/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

app/controllers/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

app/models/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

app/controllers/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

config/initializers/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

ОБНОВЛЕНИЕ:

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

Одна нота:

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

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


person jay    schedule 20.01.2011    source источник
comment
У вас есть вопрос? я не понимаю...   -  person Anton    schedule 19.05.2011
comment
Мне было трудно найти вопрос в этом посте, но в конце концов я его нашел. Однако, если вы пришли к решению самостоятельно, пожалуйста, отправьте сообщение как ответ (и отметьте его как принятое), а не обновляйте вопрос.   -  person Kev    schedule 25.05.2011
comment
@jay, я бы сказал, что вам нужно быть более осторожным при редактировании своих сообщений в будущем - при ответе на ваш вопрос посетитель теперь приветствует ОБНОВЛЕНИЕ: нашел другое решение ... что очень сбивает с толку.   -  person Ben    schedule 15.07.2011
comment
Отредактировано, чтобы исправить всю путаницу.   -  person WattsInABox    schedule 20.07.2011
comment
Я начал с драгоценного камня, чтобы помочь исправить такие проблемы: github.com/amaabca/mime_version Пожалуйста, позвольте мне узнайте, пробовали ли вы это, и какие у вас могут быть отзывы.   -  person cbrulak    schedule 08.06.2012


Ответы (2)


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

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

  • Массив Monkey-patch для реализации метода, который вернет (старый) v1 JSON обратно
person Daan    schedule 23.08.2011
comment
да, это была моя ТАКАЯ нубность. надеюсь с помощью правок будет понятнее... - person jay; 01.09.2011

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

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

person Elliot Winkler    schedule 26.08.2011
comment
при исследовании этого я рассмотрел как использование пространства имен маршрутов, так и типа контента. спецификатор типа содержимого кажется более RESTful, поскольку ресурс всегда определяется с одним uri как /widget/1 вместо двух uri /widget/1.json и /widget/1.xml. я полностью согласен с точкой зрения, что представление должно быть представлением, и рабль тоже выглядит очень интересно, спасибо, что указали на это! - person jay; 01.09.2011