You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 21:50:13