如何在 React 中使用窗口的 beforeunload 事件执行 axios 代码

Posted

技术标签:

【中文标题】如何在 React 中使用窗口的 beforeunload 事件执行 axios 代码【英文标题】:How to execute axios code using the window's beforeunload event in React 【发布时间】:2020-12-16 10:25:41 【问题描述】:

我正在使用 React 和 php,我需要它来做一些特定的事情。我正在使用 Axios 向我的 PHP 页面发送请求,然后更改我的数据库。如果用户关闭页面或浏览器,我需要更新我的 mysql 数据库表,将 is_logged 值从 true 更改为 false .执行此操作的代码在窗口的 beforeunload 事件中设置。但是,数据库永远不会更新。我正在尝试做的事情在 React 中是否可行?

这是我的 React 组件代码:

import React,  useState, useEffect  from 'react';
import axios from 'axios';
import Viewers from './Viewers';

const Live = ( match ) => 

  window.addEventListener("beforeunload", () => 
    axios.get('http://localhost/live-streaming-app/get_viewers.php?=' + token)
      .then(response => console.log(response))
  )

  // Set token from url
  const token = match.params.token

  //Get the fullname and update logged_in to true
  const [fullname, setFullname] = useState("")

  useEffect(() => 
    axios.get('http://localhost/live-streaming-app/get_user.php?token=' + token)
      .then(response => 
        setFullname(response.data.fullname)
        console.log("Data", response.data)
      )
      .catch(error => console.log(error))

    return () => 
      axios.get('http://localhost/live-streaming-app/get_viewers.php?=' + token)
        .then(response => console.log(response))
        .catch(error => console.log(error))
    
  , [token])

  //Jsx for when user is unauthorised
  const rejected = (
    <div className="container text-center pt-5">
      <h1>Rejected Connection</h1>
      <p>You are unauthorised to view this page</p>
    </div>
  )

  let my_render = ""

  if (fullname && fullname != null) 
    my_render = <Viewers fullname=fullname />
   else 
    my_render = rejected
  

  return my_render
;

export default Live;

这里是调用的 PHP 页面:

<?php
require 'connect.php';

$token = $_GET['token'];

$sql = "UPDATE `viewers` SET `logged_in`='false' WHERE `live_token`='$token'";
if(mysqli_query($con, $sql))
  http_response_code(201);

else 
  http_response_code(422);


exit;

【问题讨论】:

【参考方案1】:

需要处理的问题:

请求是否正在发送?

尝试运行

axios.get('http://localhost/live-streaming-app/get_viewers.php?=')
  .then(response => console.log(response))

在您的浏览器控制台中查看控制台中是否记录了任何内容。检查开发工具中的网络选项卡以查看是否发送了请求。如果请求被发送,那么请求的发送工作。如果不是,那么您的 URL 存在一些问题,您需要修复。

令牌

发送此请求

axios.get('http://localhost/live-streaming-app/get_viewers.php?=' + token)
  .then(response => console.log(response))

表示空字符串的值为token。另一方面,您的 PHP 代码假定有一个名为 token 的参数:

<?php
require 'connect.php';

$token = $_GET['token']; //This is the line which assumes that a parameter called token exists

$sql = "UPDATE `viewers` SET `logged_in`='false' WHERE `live_token`='$token'";
if(mysqli_query($con, $sql))
  http_response_code(201);

else 
  http_response_code(422);


exit;

为了符合 PHP 代码的假设,您需要指定 token 是名为 token 的参数的值:

  window.addEventListener("beforeunload", () => 
    axios.get('http://localhost/live-streaming-app/get_viewers.php?token=' + token)
      .then(response => console.log(response))
  )

上面的修复应该可以解决您所询问的问题,但我们不要止步于此。

SQL 注入

如果我从浏览器控制台(或 cURL)发送此请求(除非您创建备份,否则不要运行它)怎么办:

axios.get("http://localhost/live-streaming-app/get_viewers.php?token=';delete from viewers where ''='")
  .then(response => console.log(response))

?

然后你的 PHP 代码会执行这个:

UPDATE `viewers` SET `logged_in`='false' WHERE `live_token`='';delete from viewers where ''=''

从您的数据库中删除所有viewers。使用PDO 参数化您的查询,使您的查询免受此类攻击。

总结

React 是一个 javascript 框架,这意味着任何 Javascript 都可以,您的客户端也可以。确实,某些功能不一定以 React 方式可用,但假设 Javascript 能够做到这一点,您不必担心这些功能是可能的。我倾向于认为当客户端发生很多事情时,客户端框架将变得不那么有用,我认为客户端框架流行的主要原因是大多数程序员没有意识到有多少Javascript 能够做的事情。我并不是说永远不应该使用客户端框架。我只是说我看到程序员是客户端框架和技术的盲目粉丝,最终破坏了他们从事的项目。因此,当您选择客户端技术堆栈时,值得检查客户端所需的功能、在没有客户端框架的情况下实现它所需的时间以及使用您可能考虑的每个框架实现它所需的时间使用。比较两者并考虑截止日期和财务能力。如果您找不到使用客户端框架的明显、非常好的理由,那么我建议您不要使用它。因为,归根结底,如果在开发过程中发现由于某种原因需要使用客户端框架,您可以轻松地从不使用框架的位置转移到它。但是,如果您使用的是客户端框架并且结果证明它对您的项目不可行,那么重构整个项目以不使用该框架通常意味着重新实现大部分客户端。而这类问题往往不会在紧急情况下显现出来。

【讨论】:

【参考方案2】:

不保证beforeunload事件中执行的异步动作一定会完成,axios使用异步的方式发出请求。

您可能可以使用良好的旧 XHR 来制作 synchronous request。如果您转到标记为Adapting Sync XHR use cases to the Beacon API 的部分,他们将讨论在卸载期间保持请求活动的策略,因为不推荐使用同步 XHR。

请注意,同步 HTTP 请求通常不是一个好主意,因为它们会在完成时呈现页面无响应。我不确定卸载期间同步请求的行为是什么。

同步请求示例

logUser () 
    var params = JSON.stringify( locked_by: '' );
    let xhr = new XMLHttpRequest()
    xhr.open('PUT',Url, false) // `false` makes the request synchronous
    xhr.setRequestHeader("Authorization", 'Bearer ' + localStorage.getItem('app_access_token'))
    xhr.setRequestHeader("Content-length", params.length);
    xhr.setRequestHeader("Content-type", "application/json; charset=utf-8")
    xhr.responseType = 'arraybuffer'
    xhr.onreadystatechange = function() //Call a function when the state changes.
    if(xhr.readyState == 4 && xhr.status == 200) 
        alert(xhr.responseText);
    


注意

没有可靠的方法可以在选项卡/窗口关闭时执行某些操作。这违反了“关闭”选项卡即释放资源的原则。

我处理你的情况的方法是让前端持有一些在标签关闭时自行消失的东西。

要么打开一个可以用于许多其他目的的 websocket,当它死掉并且在几秒钟内没有返回时,你知道客户端已断开连接,或者在选项卡打开时发送常规 ping错过了几次 ping,您可以放心地认为客户端已断开连接。

【讨论】:

以上是关于如何在 React 中使用窗口的 beforeunload 事件执行 axios 代码的主要内容,如果未能解决你的问题,请参考以下文章

开发 React Native Apps 时如何在 Android 模拟器窗口中重新加载?

React 窗口如何迭代你的数据?

OAuth 弹窗跨域安全 React.js

OAuth 弹窗跨域安全 React.js

React app无法登录到控制台或终端窗口

React-Native Spotify SDK iOS:关闭身份验证窗口