如何在Deno.serve中处理用户中断连接的场景?
Deno中检测请求中断与处理进程终止问题
问题描述
我基于Deno官网的HTTP服务器示例简化了代码,如下:
const abortController = new AbortController(); Deno.addSignalListener('SIGINT', () => { abortController.abort(); }); Deno.serve( { port:3000, signal: abortController.signal }, async (req) => { if (req.body) { try { console.log('Start'); const body = await req.formData(); for (const key of body.keys()) { console.log(key); } console.log('Never reached, when user abort.'); } catch { // Will not be triggered on abort. console.log('Error on await req.formData()'); } } return new Response("Hello, World!"); } );
遇到两个问题:
- 官网提示用户在请求体完全接收前断开连接时,
req.text()这类读取请求体的调用可能失败,但我无法捕获该场景,Deno能否检测到连接终止? - 添加SIGINT处理后,若
await req.formData()执行成功,按Ctrl+C可正常终止服务;但如果用户取消上传(请求体读取未完成),按Ctrl+C后进程仍会运行,直到关闭终端。
解决方案
1. 捕获客户端断开连接的异常
当客户端在上传中途断开连接时,req.formData()会抛出类型为AbortError的DOMException,你可以在catch块中针对性捕获并处理:
try { console.log('Start'); const body = await req.formData(); for (const key of body.keys()) { console.log(key); } console.log('Completed'); } catch (err) { if (err.name === 'AbortError') { console.log('客户端已断开连接,请求终止'); } else { console.log('读取表单数据出错:', err); } }
另外,也可以通过req.signal监听客户端的中断事件,提前做清理:
req.signal.addEventListener('abort', () => { console.log('客户端主动终止请求'); });
2. 解决SIGINT无法终止进程的问题
当请求处理卡在await req.formData()时,即使触发了服务器的终止信号,正在进行的请求读取操作仍会阻塞进程退出。可以通过Promise.race将服务器的终止信号与请求体读取操作绑定,让终止信号能打断读取流程:
修改后的完整代码如下:
const abortController = new AbortController(); Deno.addSignalListener('SIGINT', () => { abortController.abort(); }); Deno.serve( { port: 3000, signal: abortController.signal }, async (req) => { if (req.body) { try { console.log('Start'); // 创建终止信号的Promise,与读取请求体的Promise竞争 const abortPromise = new Promise((_, reject) => { abortController.signal.addEventListener('abort', () => { reject(new DOMException('服务器正在终止', 'AbortError')); }, { once: true }); }); // 谁先完成就执行谁 const body = await Promise.race([req.formData(), abortPromise]); for (const key of body.keys()) { console.log(key); } console.log('Completed'); } catch (err) { if (err.name === 'AbortError') { console.log(err.message); } else { console.log('读取表单数据出错:', err); } } } return new Response("Hello, World!"); } );
这样修改后,无论请求体是否读取完成,按Ctrl+C都会触发终止信号,打断正在进行的读取操作,让进程正常退出。
内容的提问来源于stack exchange,提问作者user9847212
相关产品推荐
相关产品推荐

