jQuery.ajax – Get in Firefox, Post in Internet Explorer

by Justin Ball on July 8th, 2009

The docs for jQuery.ajax indicate that by default the request is made using 'GET'. I would still recommend setting the type to 'GET'. We just deployed a site and found that for some reason some versions of Internet Explorer were doing a POST instead of a GET. This is problematic with a Ruby on Rails site since the same url routes to different locations based on the HTTP verbs. We couldn't figure out why we were getting a bunch of incorrect create requests until we realized the before stated issue.

I suck and I am a liar. I _thought_ that adding GET to the ajax request fixed it but in fact it did not. I spent most of the night going WTF without any LOL while my PPH started going through the roof. The air started to stink from the foul language. No wonders I am losing all my hair.

I finally chose to gird up my loins, turn my back on the Mac, and use my PC. I fired up Visual Studio 2008 attached it to Internet Explorer and prepared a full barrage of colorful metaphors.

Actually Visual Studio does a nice job of debugging Javascript. If your problems come from Internet Explorer (and what problems don't) then the standard debug protocol is to use alert('save me');. Knock it off and get Visual Studio. I'll come out of the closet and admin I really like Visual Studio. Combine it with Resharper and you have an incredible tool for writing C# code (if you're into that sort of thing). If you don't have a copy (I get mine free by attending the Visual Studio Launch events) then you might try Visual Studio Express edition.

To debug javascript using Visual Studio simply create a web project and then put the following into default.aspx. Remember to change 'http://192.168.100.101:3000/' to the url of your application. 'http://192.168.100.101:3000/' just happens to point to a Ruby on Rails application running on my Mac.

 
 
<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
    <meta http-equiv="refresh" content="0;url=http://192.168.100.102:3000/">
 
</head>
<body>
<form id="form1" runat="server">
<div>
    </div>
</form>
 
</body>
</html>
 

Hit the run button in Visual Studio. It should fire up Internet Explorer. You'll notice a bunch of Javascript files magically show up in the Solution Explorer. Just click on the desired file and add a breakpoint. When you hit that code you'll have all of Visual Studio's debugging tools available to you. Pretty cool.

One other thing. If you are debugging into jQuery.js like I was doing you'll also want to get the development version that is not compressed.

Back to our story.

As I stated above Internet Explorer was sending 'POST' requests instead of 'GET' requests. This is naughty when a Ruby on Rails application lives on the other end since the request is then routed to 'create' instead of 'show' or 'index'. The result is lots of errors and unhappy programmers and lots of WTFs.

At first I was sure there was something wrong with jQuery. Forgive me my heresy. It is the one true Javascript library. I repent.

Turns out I had the following code in one of my Javascript files:

 
jQuery(document).ajaxSend(function(event, request, settings) {
  if (typeof(AUTH_TOKEN) == "undefined") return;
  settings.data = settings.data || "";
  settings.data += (settings.data ? "&" : "") + "authenticity_token=" + encodeURIComponent(AUTH_TOKEN);
});
 

I pulled this code off the net. It looked and worked like magic. Turns out there something dark hidden within.

I found a line in jQuery.js that does this:

 
  // If data is available, append data to url for get requests
  if ( s.data && type == "GET" ) {
    s.url += (s.url.match(/\?/) ? "&" : "?") + s.data;
 
    // IE likes to send both get and post data, prevent this
    s.data = null;
  }
 

The IE comment is key here. Later on jQuery gives you a chance to say your piece by doing this:

 
  if ( s.global )
    jQuery.event.trigger("ajaxSend", [xhr, s]);
 

Since that code is called after 's.data = null;' it has a chance to put stuff into 's.data' which is exactly what my code did.

Stupid WTF !#@$!@#$ code. There is a lesson here. Something about being careful about what you pull off the net. Be careful about global code. I'm sure I should learn it but it's late so maybe tomorrow.

Here's the fix to all my effort:

 
jQuery(document).ajaxSend(function(event, request, settings) {
  if (typeof(AUTH_TOKEN) == "undefined") return;
  // This next line is the key!
  if (settings.type == 'GET') return; // Don't add anything to a get request let IE turn it into a POST.
  settings.data = settings.data || "";
  settings.data += (settings.data ? "&" : "") + "authenticity_token=" + encodeURIComponent(AUTH_TOKEN);
});
 

You don't want to be adding anything to settings.data at this point or Internet Explorer will automatically turn your request into a POST regardless of anything else you have set.

To pay homage to Visual Studio and express my joy at finding this one line of code that fixed my problems I wrote half this post on my PC (in Firefox) while I was waiting for my Mac to reboot. Happy Joy.

  • coolaj86
    So how in the heck does one go about getting around this cross-site-posting issue?

    I'm trying to develop a fully cross-site data service. I was hoping to be able to use the fact that the HTTP spec doesn't disallow a GET with a body and use GET with _method=post and be able to upload files that way.

    Perhaps I'll have to look more into iframes and flash... ugh...
  • goodwill1120
    Great post. You have to force settings.type to lowercase though- by default indeed its lowercase.
  • jbasdf
    I'm not sure why it wouldn't work for you but I'd be interested to know if there are circumstances where this fails. I didn't patch jQuery, but I am using jRails.
  • Graeme Worthy
    I couldn't get your solution to work.
    Were you patching jquery itself?

    I did however, find a _different_ solution to getting jquery to talk to rails in IE.

    Rails allows a POST to act as a GET as long as it passes a parameter of _method=get along with itself.
    here was the intended request
    $.ajax({url: "/resource", type:'GET'})

    But, as stated, IE turned this into a POST

    POST /resource

    I then attempted to attach data along with it, so as to have a param of method=get
    $.ajax({url: "/resource", type:'GET', data:"_method=get"})

    POST /resource?method=get

    Which had no effect. I had to, paradoxically, ask jquery to do a post, so my data would be passed as the post's data, and not as part of the query string

    $.ajax({url: "/resource", type:'POST', data:"_method=get"})

    I hope this helps someone
  • Steffen
    Thanks a lot, man. I was wondering all morning what the heck is wrong with IE and didn't know where to start searching.
    Your solution works seamless for me. You're great. I mean it.

    Steffen
  • louis
    thanks so much for posting this -- you just saved me hours!
  • jbasdf
    Sure thing. I started out writing all this down because my memory tends to fail me on detailed problems like this. Glad it helps!
  • davidnawara
    I constantly find myself on your blog for code fixes. I love you, man. Thanks for all your effort documenting these headaches.
  • Ken
    I was having the exact problem. I develop primarily on a Mac w/Rails and Firefox. When I finally decided it was tiem to test in IE8 all of my AJAX get requests were being sent as posts. You just saved me hours of work - thank you so much!!!
  • Thanks for the code Ryan. I'll have to try this out.
  • Ryan Shaw
    sorry that didnt get formatted very well here's a pastie: http://pastie.org/540207
  • Ryan Shaw
    hey Justin,
    I feel your pain. I have also ran into some problems with this as well as some other weirdness on sending DELETE requests. This is what I came up with:

    // Save this chunk as something like jquery.rails_autehticity.js and include it after you include jquery.js
    // Rails jQuery ajaxSend()
    //
    // Original found here: http://henrik.nyh.se/2008/05/rails-authenticity...
    //
    // Seems I needed to do a few other things as well to get the ajax request sent properly,
    // and receive the proper response.
    //
    // For some reason DELETE requests were being sent to the server with Content-Type application/xml.
    // That results in a FATAL: FAILSAFE error in rails. (My action only had a respond_to for .js)
    // Looking through the jquery code, it looks like it calls open() which for some reason is changing
    // the default application/x-www-form-urlencoded to application/xml, so we force it to be correct here.
    //
    //
    (function($) {

    $(document).ajaxSend(function(event, request, settings) { //Set request headers globally

    request.setRequestHeader("Accept", "text/javascript");
    request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");

    if (settings.type.toUpperCase() == 'GET' || typeof(AUTH_TOKEN) == "undefined") return;
    // settings.data is a serialized string like "foo=bar&baz=boink" (or null)
    settings.data = (settings.data || "");
    settings.data += (settings.data ? "&" : "") + "authenticity_token=" + encodeURIComponent(AUTH_TOKEN);
    });

    })(jQuery);
blog comments powered by Disqus