Top ajax Questions

List of Tags
669
user1684978

Since the upgrade to iOS 6, we are seeing Safari's web view take the liberty of caching $.ajax calls. This is in the context of a PhoneGap application so it is using the Safari WebView. Our $.ajax calls are POST methods and we have cache set to false {cache:false}, but still this is happening. We tried manually adding a timestamp to the headers but it did not help.

We did more research and found that Safari is only returning cached results for web services that have a function signature that is static and does not change from call to call. For instance, imagine a function called something like:

getNewRecordID(intRecordType)

This function receives the same input parameters over and over again, but the data it returns should be different every time.

Must be in Apple's haste to make iOS 6 zip along impressively they got too happy with the cache settings. Has anyone else seen this behavior on iOS 6? If so, what exactly is causing it?


The workaround that we found was to modify the function signature to be something like this:

getNewRecordID(intRecordType, strTimestamp)

and then always pass in a timestamp parameter as well, and just discard that value on the server side. This works around the issue. I hope this helps some other poor soul who spends 15 hours on this issue like I did!

Answered By: Kieran ( 333)

After a bit of investigation, turns out that Safari on iOS6 will cache POSTs that have either no Cache-Control headers or even "Cache-Control: max-age=0".

The only way I've found of preventing this caching from happening at a global level rather than having to hack random querystrings onto the end of service calls is to set "Cache-Control: no-cache".

So:

  • No Cache-Control or Expires headers = iOS6 Safari will cache
  • Cache-Control max-age=0 and an immediate Expires = iOS6 Safari will cache
  • Cache-Control: no-cache = iOS6 Safari will NOT cache

I suspect that Apple is taking advantage of this from the HTTP spec in section 9.5 about POST:

Responses to this method are not cacheable, unless the response includes appropriate Cache-Control or Expires header fields. However, the 303 (See Other) response can be used to direct the user agent to retrieve a cacheable resource.

So in theory you can cache POST responses...who knew. But no other browser maker has ever thought it would be a good idea until now. But that does NOT account for the caching when no Cache-Control or Expires headers are set, only when there are some set. So it must be a bug.

Below is what I use in the right bit of my Apache config to target the whole of my API because as it happens I don't actually want to cache anything, even gets. What I don't know is how to set this just for POSTs.

Header set Cache-Control "no-cache"

Update: Just noticed that I didn't point out that it is only when the POST is the same, so change any of the POST data or URL and you're fine. So you can as mentioned elsewhere just add some random data to the URL or a bit of POST data.

Update: You can limit the "no-cache" just to POSTs if you wish like this in Apache:

SetEnvIf Request_Method "POST" IS_POST
Header set Cache-Control "no-cache" env=IS_POST
663
Mark Struzinski

I'm taking my first crack at Ajax with jQuery. I'm getting my data onto my page, but I'm having some trouble with the JSON data that is returned for Date data types. Basically, I'm getting a string back that looks like this:

/Date(1224043200000)/

From someone totally new to JSON - How do I format this to a short date format? Should this be handled somewhere in the jQuery code? I've tried the jQuery.UI.datepicker plugin using $.datepicker.formatDate() without any success.

FYI: Here's the solution I came up with using a combination of the answers here:

function getMismatch(id) {
  $.getJSON("Main.aspx?Callback=GetMismatch", 
    { MismatchId: id },

    function (result) {
      $("#AuthMerchId").text(result.AuthorizationMerchantId);
      $("#SttlMerchId").text(result.SettlementMerchantId);
      $("#CreateDate").text(formatJSONDate(Date(result.AppendDts)));
      $("#ExpireDate").text(formatJSONDate(Date(result.ExpiresDts)));
      $("#LastUpdate").text(formatJSONDate(Date(result.LastUpdateDts)));
      $("#LastUpdatedBy").text(result.LastUpdateNt);
      $("#ProcessIn").text(result.ProcessIn);
    }
  );

  return false;
}

function formatJSONDate(jsonDate) {
  var newDate = dateFormat(jsonDate, "mm/dd/yyyy");
  return newDate;
}

This solution got my object from the callback method and displayed the dates on the page properly using the date format library.

Answered By: Roy Tinker ( 688)

Eval is not necessary. This will work fine:

var date = new Date(parseInt(jsonDate.substr(6)));

The substr function takes out the "\/Date(" part, and the parseInt function gets the integer and ignores the ")\/" at the end. The resulting number is passed into the Date constructor.

575
Andrew Koester

This is something I've always been curious about, is exactly why Google prepends while(1); to their (private) JSON responses.

For example, here's a response while turning a calendar on and off in Google Calendar:

while(1);[['u',[['smsSentFlag','false'],['hideInvitations','false'],['remindOnRespondedEventsOnly','true'],['hideInvitations_remindOnRespondedEventsOnly','false_true'],['Calendar ID stripped for privacy','false'],['smsVerifiedFlag','true']]]]

I would assume this is to prevent people from doing an eval() on it, but all you'd really have to do is replace the while and then you'd be set. I would assume eval prevention is to make sure people write safe JSON parsing code.

I've seen this used in a couple other places, too, but a lot more so with Google (Mail, Calendar, Contacts, etc.) Strangely enough, Google Docs starts with &&&START&&& instead, and Google Contacts seems to start with while(1); &&&START&&&.

Does anyone know what's going on here?

Answered By: rjh ( 849)

It prevents json hijacking.

Contrived example: say Google has a URL like gmail.com/json?action=inbox which returns the first 50 messages of your inbox in JSON format. Evil websites on other domains can't make AJAX requests to get this data due to the same-origin policy, but they can include the URL via a <script> tag. The URL is visited with your cookies, and by overriding the global array constructor or accessor methods they can have a method called whenever an object (array or hash) attribute is set, allowing them to read the JSON content.

The while(1); or &&&BLAH&&& prevents this: an AJAX request at gmail.com will have full access to the text content, and can strip it away. But a <script> tag insertion blindly executes the JavaScript without any processing, resulting in either an infinite loop or a syntax error.

This does not address the issue of cross-site request forgery.

520
Sergio del Amo

I would like to upload a file asynchronously with JQuery. This is my HTML:

<span>File</span>
<input type="file" id="file" name="file" size="10"/>
<input id="uploadbutton" type="button" value="Upload"/>

And here my javascript:

$(document).ready(function () {
  $("#uploadbutton").click(function () {
    var filename = $("#file").val();

    $.ajax({
      type: "POST",
      url: "addFile.do",
      enctype: 'multipart/form-data',
      data: {
        file: filename
      },
      success: function () {
        alert("Data Uploaded: ");
      }
    });
  });
});

Instead of the file being uploaded, I am only getting the filename. Help?

Current Solution

I am using to upload files the jQuery Form Plugin

Answered By: olanod ( 316)

With HTML5 you CAN make file uploads with Ajax and Jquery. Not only that, you can do file validations(name,size,MIME-type) or handle the progress event with the html5 progress tag(or a div). Recently I had to make a file uploader but I didn't want to use flash nor Iframes or plugins and after some research I came up with the solution.

The HTML:

<form enctype="multipart/form-data">
<input name="file" type="file" />
<input type="button" value="Upload" />
</form>
<progress></progress>

First you can do some validation if you want. For example in the onChange event of the file.

$(':file').change(function(){
    var file = this.files[0];
    name = file.name;
    size = file.size;
    type = file.type;
    //your validation
});

Now the Ajax submit with the button's click.

$(':button').click(function(){
    var formData = new FormData($('form')[0]);
    $.ajax({
        url: 'upload.php',  //server script to process data
        type: 'POST',
        xhr: function() {  // custom xhr
            myXhr = $.ajaxSettings.xhr();
            if(myXhr.upload){ // check if upload property exists
                myXhr.upload.addEventListener('progress',progressHandlingFunction, false); // for handling the progress of the upload
            }
            return myXhr;
        },
        //Ajax events
        beforeSend: beforeSendHandler,
        success: completeHandler,
        error: errorHandler,
        // Form data
        data: formData,
        //Options to tell JQuery not to process data or worry about content-type
        cache: false,
        contentType: false,
        processData: false
    });
});

Now if you want to handle the progress.

function progressHandlingFunction(e){
    if(e.lengthComputable){
        $('progress').attr({value:e.loaded,max:e.total});
    }
}

As you can see, with HTML5(and some research) file uploading not only becomes possible but super easy. Try it with Chrome as some of the html5 components of the example aren't avaible in every browser.

394
lukewm

Using jQuery, how can I cancel/abort an Ajax request that I have not yet received the response from?

Answered By: meouw ( 489)

Most of the jQuery Ajax methods return an XMLHttpRequest (or the equivalent) object, so you can just use abort().

See the documentation:

  • abort Method (MSDN). Cancels the current HTTP request.
  • abort() (MDC). If the request has been sent already, this method will abort the request.
var xhr = $.ajax({
    type: "POST",
    url: "some.php",
    data: "name=John&location=Boston",
    success: function(msg){
       alert( "Data Saved: " + msg );
    }
});

//kill the request
xhr.abort()

UPDATE: As of jQuery 1.5 the returned object is a wrapper for the native XMLHttpRequest object called jqXHR. This object appears to expose all of the native properties and methods so the above example still works. See The jqXHR Object (jQuery API documentation).

319
Elliot Vargas

Im using $.post() to call a Servlet using Ajax and then use the resulting HTML fragment to replace a div element in the User's current page. However, if the session timeouts the server sends a redirect directive to send the user to the login page. Nonetheless, JQuery is replacing the div element with the contents of the login page, forcing the user's eyes to witness a rare scene indeed.

How can I manage a redirect directive from an Ajax call?

  • jQuery 1.2.6
Answered By: Steg ( 264)

I read this question and implemented the approach that has been stated regarding setting the response status code to 278 in order to avoid the browser transparently handling the redirects. Even though this worked, I was a little dissatisfied as it is a bit of a hack.

After more digging around, I ditched this approach and used JSON. In this case, all responses to ajax requests have the status code 200 and the body of the response contains a JSON object that is constructed on the server. The javascript on the client can then use the JSON object to decide what it needs to do.

I had a similar problem to yours. I perform an ajax request that has 2 possible responses: one that redirects the browser to a new page and one that replaces an existing HTML form on the current page with a new one. The jquery code to do this looks something like:

$.ajax({
    type: "POST",
    url: reqUrl,
    data: reqBody,
    dataType: "json",
    success: function(data, textStatus) {
        if (data.redirect) {
            // data.redirect contains the string URL to redirect to
            window.location.href = data.redirect;
        }
        else {
            // data.form contains the HTML for the replacement form
            $("#myform").replaceWith(data.form);
        }
    }
});

The JSON object "data" is constructed on the server to have 2 members: data.redirect and data.form. I found this approach to be much better.

257
John Millikin

I've seen a couple questions around here like How to debug RESTful services, which mentions:

Unfortunately that same browser won't allow me to test HTTP PUT, DELETE, and to a certain degree even HTTP POST.

I've also heard this, that browsers support only GET and POST, from some other sources like:

However, a few quick tests in Firefox show that sending PUT and DELETE requests works as expected -- the XMLHttpRequest completes successfully, and the request shows up in the server logs with the right method. Is there some aspect to this I'm missing, such as cross-browser compatibility or non-obvious limitations?

Answered By: Matthew Murdoch ( 170)

HTML forms (up to HTML version 4 and XHTML 1) only support GET and POST as HTTP request methods. A workaround for this is to tunnel other methods through POST by using a hidden form field which is read by the server and the request dispatched accordingly.

However, for the vast majority of RESTful web services GET, POST, PUT and DELETE should be sufficient. All these methods are supported by the implementations of XMLHttpRequest in all the major web browsers (IE, Firefox, Opera).

236
Guichard

I have found two plugins that enable the browser's back button to work across ajax interactions, but I can't determine which is better and why. The two plugins are history_remote and the history.

The history plug in is simpler and seems to provide all the functionality I need, but I'm not sure I understand enough about them to make an intelligent decision. For my application, I need the plugin to allow the back button to work through ajax interactions, and I need to be able to bookmark the page at any point through the interactions.

Which plug in is best in this scenario and why? Are there any other plug ins that I missed that might be better? Are there any limitations to these plugins that I'm missing (do they not work in certain situations etc)? Any information would be greatly appreciated.

Answered By: Rostislav ( 111)

jQuery Address provides strong cross-browser support for browser history and Ajax crawling:

215
Artem Tikhomirov

I have a javascript widget which provides standard extension points. One of them is the beforecreate function. It should return false to prevent an item from being created.

I've added an AJAX call into this function using jQuery:

beforecreate: function (node, targetNode, type, to) {
  jQuery.get('http://example.com/catalog/create/' + targetNode.id + '?name=' + encode(to.inp[0].value),

  function (result) {
    if (result.isOk == false) alert(result.message);
  });
}

But I want to prevent my widget from creating the item, so I should return false in the mother-function, not in the callback. Is there any way to perform a synchronized AJAX request using jQuery or any other API? Thanks.

Answered By: Adam Bellaire ( 260)

From the Jquery docs: you specify the async option to be false to get a synchronous Ajax request. Then your callback can set some data before your mother function proceeds.

Here's what your code would look like if changed as suggested:

beforecreate: function(node,targetNode,type,to) {
    jQuery.ajax({
         url:    'http://example.com/catalog/create/' 
                  + targetNode.id 
                  + '?name=' 
                  + encode(to.inp[0].value),
         success: function(result) {
                      if(result.isOk == false)
                          alert(result.message);
                  },
         async:   false
    });          
}

I am using Ajax and hash for navigation. Is there a way to check if the window.location.hash changed like this?

http://example.com/blah#123 to http://example.com/blah#456

It works if I check it when the document loads. But if I have #hash based navigation it doesn't work when I press the back button on the browser (so I jump from blah#456 to blah#123). It shows inside the address box, but I can't catch it with JavaScript.

Answered By: meandmycode ( 225)

The only way to really do this (and is how the 'reallysimplehistory' does this), is by setting an interval that keeps checking the current hash, and comparing it against what it was before, we do this and let subscribers subscribe to a changed event that we fire if the hash changes.. its not perfect but browsers really don't support this event natively.


Update to keep this answer fresh:

If you are using jQuery (which today should be somewhat foundational for most) then a nice solution is to use the abstraction that jQuery gives you by using its events system to listen to hashchange events on the window object.

$(window).on('hashchange', function() {
  .. work ..
});

The nice thing here is you can write code that doesn't need to even worry about hashchange support, however you DO need to do some magic, in form of a somewhat lesser known jQuery feature jQuery special events.

With this feature you essentially get to run some setup code for any event, the first time somebody attempts to use the event in any way (such as binding to the event).

In this setup code you can check for native browser support and if the browser doesn't natively implement this, you can setup a single timer to poll for changes, and trigger the jQuery event.

This completely unbinds your code from needing to understand this support problem, the implementation of a special event of this kind is trivial (to get a simple 98% working version), but why do that when somebody else has already.