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 进行跨域处理的情况。