jQuery AJAX producing 304 responses when it shouldn't
Asked Answered
D

3

28

This really has me scratching my head. Namely because it only happens in IE, not Firefox, and I was under the impression that jQuery was effectively browser neutral. I've been cracking at this thing for the past few hours and have nailed down, at least, what is happening.

This jqGrid:

$("#DocumentListByPartRecordsGrid").jqGrid(
          {
            datatype: 'local',            
            colNames: ['<b>Id</b>', '<b>Document Name</b>', '<b>Document Type</b>', '<b>Effective Date</b>', '<b>Expiration Date</b>', '<b>Delete</b>'],
            colModel: [
                  { name: 'ASSOCIATION_ID', Index: 'ASSOCIATION_ID', resizable: true, align: 'left', hidden: true, sortable: false },
                  { name: 'FILE_NAME', Index: 'FILE_NAME', resizable: true, align: 'left', sortable: false, width:'20%' },
                  { name: 'DOCUMENT_TYPE', Index: 'DOCUMENT_TYPE', resizable: true, align: 'left', sortable: false, width:'20%' },
                  { name: 'EFFECTIVE_DATE', Index: 'EFFECTIVE_DATE', resizable: true, align: 'left', sortable: false, width:'20%' },
                  { name: 'EXPIRATION_DATE', Index: 'EXPIRATION_DATE', resizable: true, align: 'left', sortable: false, width:'20%' },
                  { name: 'Delete', Index: 'Delete',resizable: true, align: 'center', sortable: false, width:'20%' },
                  ],            
            rowNum: 15,
            rowList: [15, 50, 100],
            imgpath: '/Drm/Content/jqGrid/steel/images',
            viewrecords: true,            
            height: 162,           
            loadui: 'block',
            forceFit: true
        });

Filled by this function:

var mydata = '';    
<% if(!string.IsNullOrEmpty(Model.PCAssociatedDocuments)) { %>        
   var mydata = <%= Model.PCAssociatedDocuments %>;
<% } %>

for (var i = 0; i <= mydata.length; i++){
        jQuery("#DocumentListByPartRecordsGrid").addRowData(i, mydata[i], "last");
        }

Which is cleanly populated from the model. This is not the issue. The issue arises when using the delete functionality, which is formatted back in the controller like so:

<a class='deleteAttachment' style='cursor: pointer;' href='#' onclick='javascript:PCDocumentDelete(" + s.AssociationId.ToString() + ", " + pcId + ");'>Delete</a>

and calls this function

function PCDocumentDelete(id, pcid) {
if (confirm("Are you sure you want to delete this document?")) {
    $.blockUI({
        message: "Working...",
        css: {
            background: '#e7f2f7',
            padding: 10
        }
    });
    $.ajax(
        {
            url: '/DRM/Pc/DeleteAssociation?associationId=' + id + '&pcid=' + pcid,
            async: true,
            dataType: "json",
            success: function(result) {
                if (result.Success == true) {
                    //Reload grid                       
                    $.ajax({ async: false });
                    $("#DocumentListByPartRecordsGrid").setGridParam({ url: "/Drm/Pc/DeAssociatePartRecordsWithDocument?pcid=" + pcid, datatype: 'json', myType: 'GET', page: 1 });
                    $("#DocumentListByPartRecordsGrid").trigger("reloadGrid");
                    $.unblockUI();
                    $.showGlobalMessage('Specified document has been successfully disassociated from this part record.');
                }
                else {
                    $.unblockUI();
                    $.showGlobalMessage('An error occurred deleting the attachment.');
                }
            },
            error: function(res, stat) {
                alert(res.toString());
                alert(stat.toString());
            }
        });
    return false;
}
else {
    return false;
}

}

(showGlobalMessage is an internal function that creates a particularly formatted blockUI)

The ajax calls a method back in the controller, but the issue arises before we make it that far, so unless someone thinks it important, I'm not going to post that code. What happens is, often for inexplicable reasons, the first burst of ajax that calls PC/DeleteAssociation is coming back with a 304 (not modified) response. I know that happens on a get when nothing has changed that needs to be refreshed. But this isn't a get, it should be treated as a post, and I was under the impression that jquery.ajax was designed to, unless otherwise instructed, not generate 304 responses. I'm obviously missing something here and have been staring at it far too long to catch it myself. Anyone see what I missed? Thank you.

Dustpan answered 31/3, 2011 at 15:5 Comment(0)
M
45

I cannot see, you specifying the ajax request as a POST. So basically add:

$.ajax({ type: 'POST' });

and if that still fails (due to some browser AJAX weirdness), you could try setting cache: false:

$.ajax({ type: 'POST', cache: false });

Btw, all cache: false does, is adding some random stuff to the request URL.

EDIT1:

Regarding the

... and I was under the impression that jquery.ajax was designed to, unless otherwise instructed, not generate 304 responses

jQuery istn't generating any responses here. And the 304-header is just an HTTP header. HTTP AJAX requests are ordinary HTTP requests and may return any valid header. If the server responds with 304, the XHR object will simply serve up the locally cached response from the server. It's completely transparent for the user, though.

EDIT2:

Removed the advice about preventing caching. Seems like Voodoo to me.

EDIT3:

Added that bit in again because it apparently was necessary. Looking around the web, IE seems to be illegally caching AJAX POSTs to some extent.

Mullin answered 31/3, 2011 at 15:10 Comment(8)
Yup. @guildsbounty, if you want an Ajax call to be treated as a POST, you'll have to explicitly say that; jQuery.ajax defaults to GET. It looks a bit like that method you're calling is expecting a GET, though, given you're passing it URL parameters, even though it seems to be a delete operation, which seems a bit odd...Brainwork
weirdly enough, the voodoo bit was what worked in the end. Even the post was generating a 304. I used the caching prevention and it fixed it...thanks for the help.Dustpan
@guildsbounty: :d strange. I looked around a bit and IE seems indeed to have issues there. Edited it back in. Thanks for the feedback!Mullin
this fixed a 304 response that the server was never sending in ie9.Ambert
Good thing you added the caching back in, helped me alot :) Darn IE voodoo!Kentiga
As mentioned in the blog posted in Joshka's answer, this has the side effect of filling up the browser's cache with stuff that will never be served (leaving less room for things that should be cached). The "correct" method is to set the cache headers server side (Expires: -1).Roselba
Thank you! I was bashing my head against the keyboard for hours on this one. turns out IE9 was caching my ajax requests so it was not performing the actions that I was commanding it to do within my application. thanks again!Rinaldo
Using POST and\or disabling cache had no affect as IE is still being lazy and returning 304.Sinful
V
21
  1. Always use POST for calls to methods that modify state, not GET. This should be enough in this instance to prevent IE caching the request.
  2. IE aggressively caches ajax requests (see http://www.dashbay.com/2011/05/internet-explorer-caches-ajax/ and https://blog.httpwatch.com/2009/08/07/ajax-caching-two-important-facts/). To prevent this, you can:
    1. Add a cache busting parameter ($.ajaxSetup({ cache: false }); does this automatically.
    2. Always use POST requests (probably not appropriate in most cases).
    3. Turn on cache headers for AJAX requests server side. The first link demonstrates how to do this using in groovy. Similar methods should apply to any framework.
Vaudois answered 5/4, 2012 at 1:57 Comment(2)
Setting the global ajax parameter fixed the same issue when using the $.load() method.Reputable
Turn on cache headers for AJAX requests server sideCache-Control: no-cache or Expires: -1 for the lazy ones like me.Foresail
B
1

Cache busting is the solution!

In my case the application used a single service call with a custom header as a proxy to bridge the browser to the private part of the server (every call went to the same url, but used a custom header to tell the proxy service which service to pass it onto). Everything worked fine on Chrome and FF but IE kept returning data from the first call made on the page. cache=false option in jQuery.ajax was the fix since IE just looked at the same url being called, didn't even bother looking at whether any custom headers were used, or whether different data was even being passed in the body, and just said "oh, I know this one, here.." and gave back the response of the first call. With the cache busting technique the url looks different to IE so it sent it through.

Burghley answered 1/11, 2012 at 4:55 Comment(1)
HTTP Cache-Control would solve your problem as well.Foresail

© 2022 - 2024 — McMap. All rights reserved.