Safari双重提交ajax调用

Safari双重提交ajax调用

问题描述:

我注意到Safari 5.0.5(6533.21.1)似乎在提交重复的ajax调用。当我运行下面的减少测试用例:Safari双重提交ajax调用

// jquery 1.6 include 
$(document).ready(function() { 
    setTimeout(function(e) { 
     var req1 = $.getJSON('/api/private/customers.json'); 
     console.log('req1 sent'); 
    }, 2000); 
    setTimeout(function(e) { 
     var req2 = $.getJSON('/api/private/customers.json'); 
     console.log('req1 sent'); 
    }, 4000); 
}); 

Safari的资源板和控制台显示了两个XHR请求走出去,但我的服务器日志显示三个XHR请求进来:

XX.XX.XX.XXX - - [10/May/2011:16:50:40 -0400] "GET /api/private/customers.json HTTP/1.1" 200 183 "https://sub.mydomain.com/customers" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-us) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1" 
XX.XX.XX.XXX - - [10/May/2011:16:50:42 -0400] "GET /api/private/customers.json HTTP/1.1" 200 183 "https://sub.mydomain.com/customers" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-us) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1" 
XX.XX.XX.XXX - - [10/May/2011:16:50:42 -0400] "GET /api/private/customers.json HTTP/1.1" 200 183 "https://sub.mycomain.com/customers" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-us) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1" 

当我使用Firefox的最新版本相同的请求,我得到正确的两项请求:

XX.XX.XX.XXX - - [10/May/2011:16:52:00 -0400] "GET /api/private/customers.json HTTP/1.1" 200 183 "https://sub.mycomain.com/customers" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1" 
XX.XX.XX.XXX - - [10/May/2011:16:52:02 -0400] "GET /api/private/customers.json HTTP/1.1" 200 183 "https://sub.mycomain.com/customers" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1" 

这种行为似乎并没有在第一次请求发生,但所有后续REQ uests一式两份发送。有谁知道发生了什么事?努力检测js中的额外请求是徒劳的。

+0

你能添加下一个4秒请求?我想知道,如果第一个是第一个超时之后的第一个是2秒,然后它看起来像是显示“重复”,因为它是第一个超时更新的另一个2秒,第二个超时开始后的另一个4秒。这可能会导致它看起来像重复。 – 2011-05-10 21:15:57

+0

谢谢布莱斯。对不起,如果这个例子造成混乱。我使用超时消除了重复点击事件的可能性。他们绝对是重复的。 – user747594 2011-05-10 22:07:02

+0

我也有这个问题。 – 2013-10-16 20:00:06

添加连接:关闭头API响应解决该问题。

+1

你能解释一下吗?你在哪里添加了这段代码? – 2013-10-16 20:02:27

+0

减少投票,因为这个答案是不完整的。请分享更多细节!此外,看起来像这可能是不安全的,根据这个:[http://stackoverflow.com/questions/2954958/modifying-jquery-ajax-request-connection-header](http://stackoverflow.com/questions/2954958 /修改-jQuery的AJAX请求连接头) – user3396385 2015-11-19 19:33:11

我认为浏览器对第二个呼叫作出conditional GET request(另见304 response status)。

第一次调用没有在浏览器中没有的缓存响应,它确实是这样一个正常的请求。
第二个请求中,浏览器首先执行一个条件GET请求,并且在看到它的缓存响应已过期时,它必须重复GET请求。

据我所知,jQuery有一个内置的修复(它会自动附加一个参数到请求url,类似_=123456789)。我不知道为什么这不起作用。

你可以尝试用手工追加请求PARAM这样的:'/api/private/customers.json?v='+(new Date().getTime())
或者你可以尝试使用jQuery.ajaxcache:false,并dataType:'jsonp'

您可以打开Safari浏览器的开发者工具(Web检查),并检查网络选项卡。它会告诉你更多关于请求和响应的信息。

+0

我不完全确定Safari有Network标签,因为我没有使用它。但Chrome的确如此,它们都是基于WebKit的,并且在这个角度上应该是相似的...... – 2011-05-10 21:46:46

+0

谢谢。我尝试了这些选项,并进行了检查以确保过期头文件被设置为过期(他们是)。有趣的是,_参数对于重复请求是相同的(例如两个_ = 1305064165485值)。 – user747594 2011-05-10 21:52:03

+0

预计最后两个请求具有相同的网址。 jQuery只生成两个请求(每个请求都有不同的_ = timestamp值)。但是对于第二个jQuery请求,浏览器进行干预,并且调用2个调用(一个cond.get和一个真正的GET)。这对javascript方面是透明的(它看起来只是一个请求)。这个_技巧应该是让这两个url不同,并诱使浏览器认为jQuery创建的两个请求是不同的。显然,Safari忽略了请求参数,并且只比较了基本url ... – 2011-05-10 22:02:53

我想我们不会知道它是一个错误还是一个功能......无论如何,Safari(如10.0.2版本)仍然执行conditionnal request,正如Dan Manastireanu所解释的那样。 我发现请求不重复的方式是使用当前请求时间设置一个'If-Unmodified-Since'标头。 (或者,您也可以在URL添加时间戳参数,如Dan Manastireanu再次mentionned)

我downvoted的setRequestHeader('Connection', "close"),因为它是一个forbidden header name并抛出一个Refused to set unsafe header "Connection"错误。

因此,一个基本要求是这样的:

var get = function(url, callbackSucess, callbackError) { 
    var request = new XMLHttpRequest(); 
    request.open('GET', url, true); 
    request.timeout = 10000; 

    var now = new Date().getTime(); 
    request.setRequestHeader('If-Unmodified-Since', now); 

    request.onreadystatechange = function() { 
     if (request.readyState !== 4) { 
      return; 
     } 
     if (request.status === 200) { 
      callbackSucess(request.status); 
     } else { 
      callbackError(request.status); 
     } 
    }; 
    request.onerror = function (error) { 
     callbackError(error); 
    }; 
    request.ontimeout = function() { 
     request.abort(); 
    }; 
    request.send(null); 
}