多多读书
646 字
3 分钟
axios 请求中断,服务端能接收到吗?

在日常的开发实践中,我们时常因各种原因需要中断发出的请求。这种操作一般在客户端执行,并且往往我们不会过多考虑服务端是否能接收到这一中断信号,以及服务端是否会针对中断进行相应的处理。今天就来探索一下服务端能否接收到请求中断的信号,并做出处理。

以使用 Go 语言的 gin 框架为例,首先展示一个处理 GET 请求的简单服务端代码:

app.GET("/abort", func(c *gin.Context) {
  select {
  case <-time.After(20 * time.Second):
    fmt.Println("end")
    return
  case <-c.Request.Context().Done():
    fmt.Println("abort")
    return
  }
})

这段代码中,time.After用于在 20 秒后结束请求,而c.Request.Context().Done()则在客户端请求取消时触发。

接下来,我们使用axios在客户端发起请求,并在 5 秒后中断这一请求:

const axios = require('axios')

const controller = new AbortController()

axios.get('http://localhost:3001/abort', {
  signal: controller.signal,
})

setTimeout(() => {
  console.log('aborting')
  controller.abort()
}, 5000)

结果如图所示:

从服务端的日志中,可以看到 5 秒后输出了“abort”,显示服务端成功接收到了中断信号。然而,当我们将服务端改为接收 POST 请求时,情况变得有些不同。

app.POST("/abort", func(c *gin.Context) {
  // 请求处理
})

结果如图所示,请求并不是在预期的 5 秒内结束,而是在 20 秒后才结束。

为什么会这样?经过各种文档的查阅和探索,最终问题定位在请求体(body)处理差异上,在 POST 请求中,服务器可能需要等待接收和处理请求体(body)。如果这个过程没有正确地与请求的上下文(Context)集成,那么在请求体处理期间,服务器可能不会响应中断信号。

app.POST("/abort", func(c *gin.Context) {
  body, _ := io.ReadAll(c.Request.Body)
  fmt.Println("body: ", string(body))
  // 请求处理
})

加入对 body 的处理后,发现服务端可以正确地监听到中断信号并作出相应处理。

那么猜测一下 Node.js 作为服务端的话,会不会也有这个问题呢?Node.js 作为服务端可以这样写:

const http = require('node:http')

const server = http.createServer((req, res) => {
  const timer = setTimeout(() => {
    console.log('end')
    res.end('end')
  }, 20000)

  req.on('close', () => {
    clearTimeout(timer)
    console.log('close')
    res.end('close')
  })
})

server.listen(3001, () => {
  console.log('Server is running on port 3001')
})

Node.js 能够通过监听close事件来响应请求中断,且在测试中没有发现任何监听失败的情况。但是,如果中间有代理服务器,不管是 Go 还是 Node.js 都无法监听到请求中断,如 vite 配置了 proxy 进行跨域处理的情况。

axios 请求中断,服务端能接收到吗?
https://fuwari.vercel.app/posts/20231126/
作者
我也困了
发布于
2023-11-26
许可协议
CC BY-NC-SA 4.0