2010-12-17 1 views
7

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

# 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 , Как я могу это сделать?

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

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

Есть несколько способов я Рассмотренные решить эту проблему, но я не могу понять, как сделать их работу:

  • Override 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's responders gem, чтобы было достаточно respond_with(@users).

ответ

1

Благодаря samuelkadolph за помощь в IRC канале #rubyonrails сегодня. Он обеспечил решение в this gist, скопированный ниже для удобства:

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).

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

Обновление: Я пробовал это в своем приложении и работает с некоторыми незначительными изменениями. Вот версия, я использую сейчас в 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

Это немного «низкотехнологичный» по сравнению с решением для reponder, но как только создать частный метод в вашем 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 
8

Обратите внимание, что технически неверно отображать 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 дополнительного массив в результате оператора знака.

+0

Спасибо, это очень хороший момент! – evanrmurphy 2011-03-11 20:22:01

3

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

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

https://github.com/rwilcox/rack_jsonp_example

+1

Это потрясающий камень, который вряд ли требует интеграции. Очень чистое и элегантное решение. Не могу вас поблагодарить. – plainjimbo 2012-05-02 00:29:01

0

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

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

 Смежные вопросы

  • Нет связанных вопросов^_^