Как высушить контроллеры Rails 3, переопределив такие методы, как response_with? - PullRequest
7 голосов
/ 17 декабря 2010

Я пытаюсь создать JSONP API для моего приложения на Rails 3.Прямо сейчас в моих контроллерах у меня есть много действий, которые следуют этому шаблону:

# This is from my users_controller.rb, as an example

def index
  @users = User.all
  respond_with(@users, :callback => params[:callback])
end

Хотя это работает как есть, я хотел бы высушить его, не повторяя :callback => params[:callback] в каждомпризыв действия к respond_with.Как я могу это сделать?

Обновление: Одна вещь, которую я понял, является уродливой из моего кода выше, это то, что опция :callback => params[:callback] будет передана для любого формата ответа, а не толькоJSON.Следующий код, вероятно, более корректен:

def index
  @users = User.all
  respond_with(@users) do |format|
    format.json { render :json => @users, :callback => params[:callback]}
  end
end

Существует несколько способов решения этой проблемы, но я не могу понять, как заставить их работать:

  • Переопределить render (возможно, в контроллере приложения), чтобы он принимал параметр :jsonp, который автоматически включает параметр :callback => params[:callback].Таким образом, я мог бы изменить приведенный выше код на следующий, который несколько короче:
def index
  @users = User.all
  respond_with(@users) do |format|
    format.json { render :jsonp => @users}
  end
end
  • Создать ответчик, который переопределяет to_json, чтобы решить мою проблему.Таким образом, я мог пропустить блок и просто позвонить respond_with(@users, :responder => 'MyResponder'), чтобы решить проблему.Или, возможно, я мог бы включить этот код в ответчик приложения, используя plataformatec gems , чтобы сам по себе было достаточно respond_with(@users).

Ответы [ 5 ]

8 голосов
/ 10 марта 2011

Обратите внимание, что технически некорректно отображать JSON с параметром обратного вызова, поскольку вы получаете ответ JavaScript (вызов функции для обратного вызова JSON-P), а не результат JSON. Так что если у вас есть

render :json => my_object, :callback => params[:callback]

и приходит запрос на /users?callback=func, Rails ответит

func({…})

с типом контента application/json, что неверно, поскольку приведенный выше ответ явно не JSON, а JavaScript.

Я использую решение

def respond_with_json(item)
  respond_with do |format|
    format.json { render :json => item }
    format.js   { render :json => item, :callback => params[:callback] }
  end
end

, который отвечает правильно с или без обратного вызова. Применяя это к вышеупомянутому решению, мы получаем:

def custom_respond_with(*resources, &block)
  options = resources.extract_options!

  if params[:callback]
    old_block = block
    block = lambda do |format|
      old_block.call(format) if block_given?
      format.js { render :json => resources[0], :callback => params[:callback] }
    end
  end

  respond_with(*(resources << options), &block)
end

Также обратите внимание на исправление resources[0], в противном случае в результате вы получите resources в дополнительном массиве в результате оператора splat.

3 голосов
/ 20 апреля 2011

Это драгоценный камень, который может сделать это для: rack-jsonp-middleware .

Инструкции по установке довольно скудны на сайте, но я создал небольшой проект Rails, который используетэто - что вы можете взглянуть на коммиты и посмотреть, что я сделал, чтобы запустить промежуточное ПО.

https://github.com/rwilcox/rack_jsonp_example

1 голос
/ 18 декабря 2010

Спасибо Самуэлькадольфу за помощь, оказанную мне сегодня на IRC-канале #rubyonrails.Он предоставил решение в этой сути , скопированное ниже для удобства:

def custom_respond_with(*resources, &block)
  options = resources.extract_options!

  if options[:callback]
    old_block = block
    block = lambda do |format|
      old_block.call(format) if block_given?
      format.json { render :json => [] }
    end
  end

  respond_with(*(resources << options), &block)
end

Я еще не пробовал это в своем приложении, но вижу, что оно должно работать.Он также подтвердил, что я могу аналогичным образом переопределить сам метод respond_with, просто изменив название этого метода и изменив последнюю строку определения на super(*(resources << options), &block).

Я думаю, что это будет работать для меня.Тем не менее, мне все еще интересно знать, как написать собственный респондент для выполнения этой работы.(Это было бы более элегантное решение, ИМХО.)

Обновление: Я попробовал это в своем приложении, и оно работает с некоторыми незначительными изменениями.Вот версия, которую я сейчас использую в разделе private моего ApplicationController, предназначенная для автоматического предоставления опции :callback => params[:callback] для запросов JSON:

def custom_respond_with(*resources, &block)
  options = resources.extract_options!

  if params[:callback]
    old_block = block
    block = lambda do |format|
      old_block.call(format) if block_given?
      format.json { render :json => resources, :callback => params[:callback] }
    end
  end

  respond_with(*(resources << options), &block)
end

Обратите внимание, что мне пришлось изменить if options[:callback]на if params[:callback], чтобы заставить его работать.

1 голос
/ 17 декабря 2010

По сравнению с решением для репликации это немного «низко-технологично», но как насчет создания частного метода в вашем appliation_controller.rb для этого?Для него будет доступна переменная params, и вы можете передать ей объект @users.

#application_controller.rb
private
  def jsonp(my_object)
    render :json => my_object, :callback => params[:callback]
  end

#controller
def index
  @users = User.all
  respond_with(@users) do |format|
    format.json { jsonp(@users)}
  end
end
0 голосов
/ 20 апреля 2011

Вы также можете проверить этот ответ . в основном вы можете создать «по умолчанию» response_to для вашего контроллера, чтобы вы могли просто сделать все ваши действия по умолчанию отвечающими на json.

это было то, что вы спрашивали?

...