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

Преамбула:

Я исследовал, как создать версию API, и нашел несколько способов сделать это.Я решил попробовать предложение Питера Уильямса и создал новые типы пантомимы вендора, чтобы указать версию и формат.Я не смог найти никакой конкретной записи для того, чтобы сделать это по «рельсовому пути», поэтому я собрал информацию из нескольких мест.Я смог заставить его работать, но есть некоторая глупость в том, как средства визуализации обрабатывают массив виджетов против экземпляра виджета в 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 в патч обезьяны на массиве.Таким образом, при наличии массива виджетов он вызывает метод экземпляра as_myproj_v1_json для каждого виджета и возвращает весь массив в нужном формате.

Одна заметка:

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

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

Ответы [ 2 ]

0 голосов
/ 26 августа 2011

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

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

0 голосов
/ 23 августа 2011

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

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

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