使用“beforeunload”/“unload”事件侦听器关闭浏览器后,使用 Fetch 发送 POST 请求不起作用

Rog*_*int 8 javascript post request fetch google-cloud-functions

对于我的一生,我无法弄清楚发生了什么。我花了一整天的时间寻找答案,但在任何地方都找不到。我正在练习编写一个函数,通过电子邮件将废弃的表单发送给我。

这是我为托管表单的本地主机运行的 index.js 文件

const inputSelector = document.getElementById("name");
const fieldSelector = document.querySelectorAll('.formfield');
let formData = {};


fieldSelector.forEach(field =>{

    field.addEventListener('input', (e) =>{

        let formField = e.target.id;
        formData[formField] = field.value;
        
    });
})


window.addEventListener('beforeunload', () =>{
    fetch('http://localhost:8080/', {
        method:'post',
        headers:{
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({
            message: formData
        })
    })
})

Run Code Online (Sandbox Code Playgroud)

请求发送到的函数是一个包含以下代码的云函数:

exports.testFetch = async (req, res) =>{
    console.log(req.method);
    if(req.method === 'OPTIONS'){
        console.log('method is option')
        res.set('Access-Control-Allow-Origin', "*")
        res.set('Access-Control-Allow-Methods', 'GET, POST');
        res.set('Access-Control-Allow-Headers', 'Content-Type');
    }

    else{
   
        console.log('full body: ', req.body);
        console.log('message: ', req.body.message);
    }

    res.send('response')

}
Run Code Online (Sandbox Code Playgroud)

现在,每当我填写表单字段,然后从该页面浏览到另一个页面时,它都会完美运行,云功能 console.logs 表单字段。但是,当我通过单击 X 关闭页面时,云功能仅 console.log“选项”和“方法是选项”。看起来它只发送 post 请求的选项部分。我花了整个下午寻找解决方案,但我无法在任何地方找到它。超级令人沮丧。当我将获取功能添加到按钮然后按下按钮时,它也可以完美工作。只是关闭浏览器后,它似乎不起作用并卡在选项中。如果您有任何提示,请告诉我!

War*_*zee 25

正如 Endless 的答案所讨论的,sendBeacon 是一个选项。但问题是您的获取请求没有使用keepalive 标志

通常,当卸载文档时,所有关联的网络请求都会中止。但是 keepalive 选项告诉浏览器在后台执行请求,即使在离开页面之后也是如此。所以这个选项对于我们的请求成功至关重要。

window.addEventListener('beforeunload', () => {
        fetch('http://localhost:8080/', {
            method:'post',
            headers:{
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({
                message: formData
            }),
            keepalive: true // this is important!
        })
    })
Run Code Online (Sandbox Code Playgroud)


End*_*ess 9

尝试使用navigator.sendBeacon

它用于在卸载期间发送请求

window.addEventListener('unload', () => {
  navigator.sendBeacon(url, data)
})
Run Code Online (Sandbox Code Playgroud)

旁注:我建议使用FormData来序列化您的数据。你可以发送它而不是一些 json。但必须使用 name 属性而不是 id。无论哪种方式你都应该这样做。

var fd = new FormData(formElement)
// if you want json anyway
var json = JSON.stringify(Object.fromEntries(fd))
Run Code Online (Sandbox Code Playgroud)

我会删除fieldSelector.forEach(...并在卸载事件时构建一次表单数据

由于您无法使用 sendBeacon 指定任何标头,因此您可以发送 Blob

var blob = new Blob([json], {type: 'application/json')
navigator.sendBeacon(url, blob)
Run Code Online (Sandbox Code Playgroud)

更多信息在这里/sf/answers/2921076791/


如果这对您不起作用,请尝试在 fetch 中使用保持活动标志{ keepalive: true }