可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
For some reason I'm getting an InvalidAuthenticityToken when making post requests to my application when using json or xml. My understanding is that rails should require an authenticity token only for html or js requests, and thus I shouldn't be encountering this error. The only solution I've found thus far is disabling protect_from_forgery for any action I'd like to access through the API, but this isn't ideal for obvious reasons. Thoughts?
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
and this is what I get in the logs whenever I pass a request to the action:
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:
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
});
回答2:
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.
回答3:
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
回答4:
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.
回答5:
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 );
}
});
回答6:
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?