Rails - InvalidAuthenticityToken для запросов json/xml

По какой-то причине я получаю InvalidAuthenticityToken при отправке почтовых запросов в мое приложение при использовании json или xml. Я понимаю, что рельсы должны требовать токен аутентификации только для запросов html или js, и поэтому я не должен сталкиваться с этой ошибкой. Единственное решение, которое я нашел до сих пор, - это отключить защиту_from_forgery для любых действий, которые я хотел бы получить через API, но это не идеально по понятным причинам. Мысли?

    def create
    respond_to do |format|
        format.html
        format.json{
            render :json => Object.create(:user => @current_user, :foo => params[:foo], :bar => params[:bar])
        }
        format.xml{
            render :xml => Object.create(:user => @current_user, :foo => params[:foo], :bar => params[:bar])
        }
    end
end

и это то, что я получаю в журналах всякий раз, когда передаю запрос на действие:

 Processing FooController#create to json (for 127.0.0.1 at 2009-08-07 11:52:33) [POST]
 Parameters: {"foo"=>"1", "api_key"=>"44a895ca30e95a3206f961fcd56011d364dff78e", "bar"=>"202"}

ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):
  thin (1.2.2) lib/thin/connection.rb:76:in `pre_process'
  thin (1.2.2) lib/thin/connection.rb:74:in `catch'
  thin (1.2.2) lib/thin/connection.rb:74:in `pre_process'
  thin (1.2.2) lib/thin/connection.rb:57:in `process'
  thin (1.2.2) lib/thin/connection.rb:42:in `receive_data'
  eventmachine (0.12.8) lib/eventmachine.rb:242:in `run_machine'
  eventmachine (0.12.8) lib/eventmachine.rb:242:in `run'
  thin (1.2.2) lib/thin/backends/base.rb:57:in `start'
  thin (1.2.2) lib/thin/server.rb:156:in `start'
  thin (1.2.2) lib/thin/controllers/controller.rb:80:in `start'
  thin (1.2.2) lib/thin/runner.rb:174:in `send'
  thin (1.2.2) lib/thin/runner.rb:174:in `run_command'
  thin (1.2.2) lib/thin/runner.rb:140:in `run!'
  thin (1.2.2) bin/thin:6
  /opt/local/bin/thin:19:in `load'
  /opt/local/bin/thin:19

Ответ 1

При включенном protect_from_forgery Rails требует токен аутентификации для любых запросов, не связанных с GET. Rails автоматически добавит токен аутентификации в формы, созданные с помощью помощников формы или ссылок, созданных с помощью помощников AJAX, поэтому в обычных случаях вам не придется об этом думать.

Если вы не используете встроенную форму Rails или помощники AJAX (возможно, вы выполняете ненавязчивый JS или используете JS MVC framework), вам нужно будет установить маркер самостоятельно на стороне клиента и отправить его вместе с вашими данными при отправке запроса POST. Вы разместили бы такую ​​строку в <head> вашего макета:

<%= javascript_tag "window._token = '#{form_authenticity_token}'" %>

Затем ваша функция AJAX отправит токен с вашими другими данными (пример с jQuery):

$.post(url, {
    id: theId,
    authenticity_token: window._token
});

Ответ 2

У меня была аналогичная ситуация, и проблема заключалась в том, что я не отправлял через заголовки правильного содержимого контента - я запрашивал text/json, и я должен был запрашивать application/json.

Я использовал curl следующее для проверки моего приложения (при необходимости измените):

curl -H "Content-Type: application/json" -d '{"person": {"last_name": "Lambie","first_name": "Matthew"}}' -X POST http://localhost:3000/people.json -i

Или вы можете сохранить JSON в локальный файл и вызвать curl следующим образом:

curl -H "Content-Type: application/json" -v -d @person.json -X POST http://localhost:3000/people.json -i

Когда я изменил заголовки типа контента вправо application/json, все мои проблемы ушли, и мне больше не нужно было отключать защиту подделок.

Ответ 4

Другой способ - исключить проверку_authenticity_token с помощью skip_before_filter в вашем приложении Rails:

skip_before_action :verify_authenticity_token, only: [:action1, :action2]

Это позволит завиткам выполнять свою работу.

Ответ 5

Добавляя ответы на andymism, вы можете использовать это для применения включения TOKEN по умолчанию в каждом запросе POST:

$(document).ajaxSend(function(event, request, settings) {
    if ( settings.type == 'POST' ||  settings.type == 'post') {
        settings.data = (settings.data ? settings.data + "&" : "")
            + "authenticity_token=" + encodeURIComponent( window._token );
    }
});

Ответ 6

Чтобы добавить к Fernando ответ, если ваш контроллер отвечает как json, так и html, вы можете использовать:

      skip_before_filter :verify_authenticity_token, if: :json_request?