You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Firefox中XHR提交数据后重定向触发NS_BINDING_ABORTED错误排查

Firefox中XHR完成后重定向仍出现NS_BINDING_ABORTED的问题解决

问题重现

在LAMP服务器上开发jsPsych实验,实验结束后通过XMLHttpRequest保存数据,代码中已等待xhr.readyState === XMLHttpRequest.DONE且xhr.status === 200确认数据保存成功后,才调用页面重定向函数,但Firefox中仍出现NS_BINDING_ABORTED错误。移除重定向逻辑后数据可正常保存,说明是重定向操作中断请求导致问题。相关代码如下:

saveData(save_url, data_dir, file_name, extension, redirect_html, redirectToNextPage);

function saveData(save_url, data_dir, file_name, extension, redirect_html, callback) {
    var xhr = new XMLHttpRequest();
    xhr.open('POST', save_url); // 'write_data.php' 是保存数据的PHP文件路径
    xhr.setRequestHeader('Content-Type', 'application/json');
    xhr.onreadystatechange = function() {
        if (xhr.readyState === XMLHttpRequest.DONE) {
            if (xhr.status === 200) {
                // 数据保存成功,触发回调
                callback(redirect_html);
            } else {
                console.error('Error saving data: ' + xhr.status);
            }
        }
    };
    xhr.send(JSON.stringify({file_name: file_name, extension: extension, data_dir: data_dir, data: jsPsych.data.get().csv()}));
}

function redirectToNextPage(redirect_html) {
    if((window.location.href).indexOf('?') != -1) {
        var variables = window.location.href.split('?')[1]; 
        redirect_html += "?" + variables;
    };

    last_trial_data = jsPsych.data.getLastTrialData().trials[0];
    if(last_trial_data["chain"] != "false"){
        window.location = redirect_html;
    };  
}

原因分析

这个错误的核心是页面卸载时机与XHR请求收尾处理存在竞争:即使XHR触发了DONE状态并返回200,Firefox可能还未完成请求连接清理、响应数据完全接收等收尾操作,此时调用window.location会立即触发页面卸载,浏览器为释放资源会中止未完全收尾的XHR连接,从而抛出NS_BINDING_ABORTED错误。

注意:此时服务器通常已成功接收并保存数据,该错误是客户端层面的连接中止,不影响数据存储,但会干扰错误日志。

解决方案

方案1:使用navigator.sendBeacon()替代XHR(推荐)

navigator.sendBeacon()是浏览器专为页面卸载前发送数据设计的API,它会将请求加入后台发送队列,确保数据发送完成后再允许页面卸载,不会被页面操作中断。修改后的代码如下:

saveData(save_url, data_dir, file_name, extension, redirect_html, redirectToNextPage);

function saveData(save_url, data_dir, file_name, extension, redirect_html, callback) {
    // 构造FormData(sendBeacon默认支持,无需手动设置Content-Type)
    const formData = new FormData();
    formData.append('file_name', file_name);
    formData.append('extension', extension);
    formData.append('data_dir', data_dir);
    formData.append('data', jsPsych.data.get().csv());

    // 尝试使用sendBeacon发送
    const isQueued = navigator.sendBeacon(save_url, formData);
    if (isQueued) {
        callback(redirect_html);
    } else {
        // 兼容不支持sendBeacon的浏览器,降级到XHR
        const xhr = new XMLHttpRequest();
        xhr.open('POST', save_url);
        xhr.setRequestHeader('Content-Type', 'application/json');
        xhr.onreadystatechange = function() {
            if (xhr.readyState === XMLHttpRequest.DONE) {
                if (xhr.status === 200) {
                    callback(redirect_html);
                } else {
                    console.error('Error saving data: ' + xhr.status);
                }
            }
        };
        xhr.send(JSON.stringify({
            file_name: file_name,
            extension: extension,
            data_dir: data_dir,
            data: jsPsych.data.get().csv()
        }));
    }
}

// 重定向函数保持逻辑不变,优化变量声明
function redirectToNextPage(redirect_html) {
    if((window.location.href).indexOf('?') !== -1) {
        const variables = window.location.href.split('?')[1]; 
        redirect_html += "?" + variables;
    }

    const last_trial_data = jsPsych.data.getLastTrialData().trials[0];
    if(last_trial_data["chain"] !== "false"){
        window.location = redirect_html;
    }
}

注意:需修改后端write_data.php的接收逻辑,改用$_POST['file_name']等方式获取参数,而非json_decode(file_get_contents('php://input'))。

方案2:给重定向添加微小延迟

若不想修改后端逻辑,可在重定向前添加短暂延迟,给Firefox足够时间完成XHR请求收尾:

function redirectToNextPage(redirect_html) {
    if((window.location.href).indexOf('?') !== -1) {
        const variables = window.location.href.split('?')[1]; 
        redirect_html += "?" + variables;
    }

    const last_trial_data = jsPsych.data.getLastTrialData().trials[0];
    if(last_trial_data["chain"] !== "false"){
        // 添加100ms延迟,给浏览器时间完成XHR收尾
        setTimeout(() => {
            window.location = redirect_html;
        }, 100);
    }
}

缺点:延迟时间需根据实际情况调整,无法保证100%兼容所有场景,但实现简单无需修改后端。

方案3:确保后端响应完全发送

在后端write_data.php中,关闭输出缓冲并强制刷新响应,确保客户端收到完整响应后再触发重定向:

<?php
// 关闭输出缓冲
ob_end_clean();
// 处理数据保存逻辑...

// 发送响应并立即刷新输出
echo json_encode(['status' => 'success']);
ob_flush();
flush();
// 立即结束脚本
exit;
?>

内容的提问来源于stack exchange,提问作者RomainP

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 19:10:55