Rails not reloading session on ajax post

Ruby on-RailsAjaxJquery

Ruby on-Rails Problem Overview


I'm experiencing a very strange problem with Rails and ajax using jQuery (although I don't think it's specific to jQuery).

My Rails application uses the cookie session store, and I have a very simple login that sets the user id in the session. If the user_id isn't set in the session, it redirects to a login page. This works with no problems. JQuery GET requests work fine too. The problem is when I do a jQuery POST - the browser sends the session cookie ok (I confirmed this with Firebug and dumping request.cookies to the log) but the session is blank, i.e. session is {}.

I'm doing this in my application.js:

$(document).ajaxSend(function(e, xhr, options) {
  var token = $("meta[name='csrf-token']").attr('content');
  xhr.setRequestHeader('X-CSRF-Token', token);
});

and here is my sample post:

$.post('/test/1', { _method: 'delete' }, null, 'json');

which should get to this controller method (_method: delete):

def destroy
  respond_to do |format|
    format.json { render :json => { :destroyed => 'ok' }.to_json }
  end
end

Looking at the log and using Firebug I can confirm that the correct cookie value is sent in the request header when the ajax post occurs, but it seems that at some point Rails loses this value and therefore loses the session, so it redirects to the login page and never gets to the method.

I've tried everything I can think of to debug this but I'm coming around to the idea that this might be a bug in Rails. I'm using Rails 3.0.4 and jQuery 1.5 if that helps. I find it very strange that regular (i.e. non-ajax) get and post requests work, and ajax get requests work with no problems, it's just the ajax posts that don't.

Any help in trying to fix this would be greatly appreciated!

Many thanks,
Dave

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

I'm going to answer my own question as I've managed to work out what was going on. I'll post it here in case it's useful to anyone else!

After investigating further, I worked out that the code that was supposed to be setting the request header with the CSRF token, wasn't. This was the original code:

$(document).ajaxSend(function(e, xhr, options) {
  var token = $("meta[name='csrf-token']").attr('content');
  xhr.setRequestHeader('X-CSRF-Token', token);
});

What was happening was that this code wasn't setting the header, Rails was receiving an Ajax request, the token didn't match and it was resetting the session. This used to raise an ActionController::InvalidAuthenticityToken error (I suppose I would have caught this earlier if an error was raised... oh well), but since Rails 3.0.4 it now just quietly resets the session.

So to send the token in the header, you have to do this (many thanks to this marvellous blog post):

$.ajaxSetup({
  beforeSend: function(xhr) {
    xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'));
  }
}); 

And now it all works as it should. Which is nice.

Solution 2 - Ruby on-Rails

I found another case:

have you set the 'csrf_meta_tag' in your application layout file?

in my case, I didn't set that tag, and met the same problem with yours.

And after setting the csrf_meta_tag in app/views/layouts/application.html.erb, everything works fine!

At last, thank you for helping me found the root cause ! thanks a lot ~

Solution 3 - Ruby on-Rails

That's what the official jquery-ujs rails adapter is for in the 3.0 series. You have to remember to keep it updated when upgrading rails versions though.

For me, upgrading from 3.0.3 to 3.0.8.rc4 meant also manually fetching the src/rails.js file from the linked repo.

Since Rails 3.1 finally made the switch to jQuery, things should auto-update in the future via the jquery-rails gem when updating rails (and using 3.1's built-in asset pipeline)

Solution 4 - Ruby on-Rails

If you still have problem after sending csrf_meta_tag correctly to the server. Be sure your session store configuration is correct.

If the :secure is set to true in your session_store.rb, the cookie (session) won't be transmitted to your http server. docs: https://api.rubyonrails.org/classes/ActionDispatch/Cookies.html

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDave HollingworthView Question on Stackoverflow
Solution 1 - Ruby on-RailsDave HollingworthView Answer on Stackoverflow
Solution 2 - Ruby on-RailsSiweiView Answer on Stackoverflow
Solution 3 - Ruby on-RailsTheDeadSeriousView Answer on Stackoverflow
Solution 4 - Ruby on-RailsKen HView Answer on Stackoverflow