Jquery Ajax 在页面加载时在 Safari 中无限重复请求

Jquery Ajax request repeating infinitely in Safari on page load

我在 Safari 中遇到 Jquery Ajax 的小问题。我在我正在开发的应用程序的一部分上使用 Ajax 到 load/refresh 静态 svg 内容。我在首次加载页面时进行初始 Ajax 调用,然后根据页面上的菜单点击,通过对同一功能的后续调用来更新地图。下面是所有代码,但首先是对问题的描述。

这一切在 Firefox 和 Chrome 中工作正常,但在 Safari 中,页面 仅在初始加载和页面重新加载时 正在生成无限循环对服务器的请求。奇怪的是,如果我停止页面加载,随后从菜单更新地图就可以正常工作。 Ajax 调用似乎正在返回错误状态——下面代码中的第二个 "alert" 正在被触发,但对话框是空的,因此没有关于问题可能是什么的其他信息。

nginx 访问日志行以及所有适用的代码片段包含在下面。 /var/log/nginx/error.log 中没有错误,即使我使用 CATALYST_DEBUG=1 设置启动 fcgi。

真的很希望有人能给我一些见解,因为我不知所措!

UPDATE -- 我 运行 在测试 Catalyst 开发服务器时遇到了同样的问题,所以这不是 nginx 或 fcgi 的问题。更有可能与 JS 相关。我还尝试禁用此处块中表示为注释的代码位,它们对行为没有影响,正如预期的那样。

1) 用于提供 SVG 图像的共享函数——请注意,我只是在此处从 Catalyst 获取静态内容的位置,而将实际内容提供给 nginx...

function loadSvgMap(mapId) {
    jQuery.ajax({
        url:      "[% c.uri_for('/maps/update_map/') %]" + mapId,
        success:  function(result) {
            if(result.isOk == false) {
                alert(result.message);
            } else {
                $("#som_map").load(result);
            }
        },
        error:    function(result, status, errorThrown) {
            alert(errorThrown);
        },
        async:    true,
        dataType: 'text'
    });
}

2) 页面加载时的初始调用

$(document).ready(function(){
    var mapId = [% map_id %];
    loadSvgMap(mapId);

/* other stuff to set the url and browser history*/
})

3) 基于菜单点击的后续更新

$(document).on("click",".map_select", function(e){
    loadSvgMap(this.name);

    /* other stuff to update browser history and location */ 
})

4) 用于页面加载的 Catalyst 处理程序:

sub map :Path :Local :Args(1) {
    my ( $self, $c , $map_id ) = @_;

    $c->stash(menus => [$c->model('DB::Menus')->build_menu($c->model('DB::Map'), $c->model('DB::MapsMenus'))]);
    $c->stash(map_id => $map_id);                             
    $c->stash(neuron => '');                                 
    $c->stash(template => 'maps/som_main.tt2');

    my %params = %{$c->request->params};
    if (exists($params{neuron})) {
        return $c->res->redirect( $c->uri_for('neuron', $map_id, $params{neuron}));
    }
}

5) 地图更新的催化剂处理程序

sub update_map :Path :Local :Args(1) {
    my ( $self, $c, $map_id ) = @_;

    my $fields = $c->model('DB::Map')->find($map_id);
    my $map_file = $fields->get_column('map_file');                               
    my $returnUrl = $c->uri_for('/static/svg', $map_file);

    $c->res->content_type("text/plain");
    $c->res->body($returnUrl);
}

6) nginx 访问日志行

10.0.2.2 - - [20/Nov/2015:16:07:35 +0000] "GET /maps/map/305 HTTP/1.1" 200 3335 "http://poppy.med.umich.edu:5000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/601.1.56 (KHTML, like Gecko) Version/9.0 Safari/601.1.56"
10.0.2.2 - - [20/Nov/2015:16:07:35 +0000] "GET /maps/get_map_info/305 HTTP/1.1" 499 0 "http://poppy.med.umich.edu:5000/maps/map/305" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/601.1.56 (KHTML, like Gecko) Version/9.0 Safari/601.1.56"
10.0.2.2 - - [20/Nov/2015:16:07:35 +0000] "GET /maps/update_map/305 HTTP/1.1" 200 51 "http://poppy.med.umich.edu:5000/maps/map/305" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/601.1.56 (KHTML, like Gecko) Version/9.0 Safari/601.1.56"

请注意,一个请求的“499”状态表示客户端挂断。这并不总是影响相同的请求——似乎只是在 Safari 中触发错误时当前 运行 中的那个。一旦最初的 "alert" 被取消(如果我选中 "Don't show more alerts..."),这三行将无限重复,直到我停止页面加载。之后,我可以使用菜单选择地图,一切正常。

为了比较,这里是来自 firefox 的请求的日志输出...

10.21.136.38 - - [20/Nov/2015:16:30:03 +0000] "GET /maps/map/1 HTTP/1.1" 200 3318 "http://10.21.136.66/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:42.0) Gecko/20100101 Firefox/42.0"
10.21.136.38 - - [20/Nov/2015:16:30:04 +0000] "GET /maps/update_map/1 HTTP/1.1" 200 48 "http://10.21.136.66/maps/map/1" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:42.0) Gecko/20100101 Firefox/42.0"
10.21.136.38 - - [20/Nov/2015:16:30:04 +0000] "GET /maps/get_map_info/1 HTTP/1.1" 200 394 "http://10.21.136.66/maps/map/1" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:42.0) Gecko/20100101 Firefox/42.0"

经过几个小时的实验,我确定这个问题实际上与 Jquery Ajax 完全无关。相反,它与我对浏览器历史记录的操作有关。具体来说,我正在监听 window.popstate 事件以触发对浏览器导航按钮的处理:

$(window).on('popstate', function(event) {
    var state = event.originalEvent.state;
    if (state) {
        window.location.assign(state.url);
    } else {
        window.location.assign(document.location.href);
    }
});

这就是问题所在,因为只要浏览器历史发生变化,无论是来自导航操作还是页面(重新)加载,Safari 都会触发 popstate 事件。 (他们在 Chrome 前一段时间修复了一些东西——来吧,Safari!遵循标准?)为了解决这个问题,我应用了以下 hack:

1) 为浏览器历史记录条目创建状态时,包含一个标记以指示我们创建了该条目:

var state = { url: newUrl, title: newTitle, loadTag: true };
window.history.pushState(state, "", newUrl); // or replaceState, for first page load

2) 在 popstate 侦听器中,检查我们的标签,除非找到它,否则什么也不做:

$(window).on('popstate', function(event) {
    var state = event.originalEvent.state;
    if (!state.loadTag) return;

    ...

问题已解决...真希望我能在花费数小时尝试修复错误问题之前发现真正的问题!