rails - InvalidAuthenticityToken for json/xml requests

情到浓时终转凉″ 提交于 2019-11-29 03:05:40
andrewle

With protect_from_forgery enabled, Rails requires an authenticity token for any non-GET requests. Rails will automatically include the authenticity token in forms created with the form helpers or links created with the AJAX helpers--so in normal cases, you won't have to think about it.

If you're not using the built-in Rails form or AJAX helpers (maybe you're doing unobstrusive JS or using a JS MVC framework), you'll have to set the token yourself on the client side and send it along with your data when submitting a POST request. You'd put a line like this in the <head> of your layout:

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

Then your AJAX function would post the token with your other data (example with jQuery):

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

I had a similar situation and the problem was that I was not sending through the right content type headers - I was requesting text/json and I should have been requesting application/json.

I used curl the following to test my application (modify as necessary):

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

Or you can save the JSON to a local file and call curl like this:

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

When I changed the content type headers to the right application/json all my troubles went away and I no longer needed to disable forgery protection.

edwardmp

This is the same as @user1756254's answer but in Rails 5 you need to use a bit more different syntax:

protect_from_forgery unless: -> { request.format.json? }

Source: http://api.rubyonrails.org/v5.0/classes/ActionController/RequestForgeryProtection.html

Fernando Kosh

Another way is to avoid verify_authenticity_token using skip_before_filter in your Rails App:

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

This will let curl to do its job.

Elad Meidar

Adding up to andymism's answer you can use this to apply the default inclusion of the TOKEN in every POST request:

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

To add to Fernando's answer, if your controller responds to both json and html, you can use:

      skip_before_filter :verify_authenticity_token, if: :json_request?
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!