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

MongoDB与Express错误处理:res未定义间歇性问题排查

问题描述

我用Node/Express搭建了一个与MongoDB交互的服务器,核心数据库操作函数withDB代码如下:

const withDB = async (operations, res) => {
    try {
        const client = await MongoClient.connect('mongodb://localhost:27017', { useNewUrlParser: true });
        const db = client.db('database-name');
        await operations(db);
        client.close();
    } catch (error) {
        res.status(500).json({ message: 'Error connecting to db', error });
    }
}

调用示例(以/api/employees接口为例):

app.get('/api/employees', async (req, res) => {
    withDB(async (db) => {        
        const tabInfo = await db.collection('users').find().toArray()
        res.status(200).json(tabInfo);
    }, res);
});

其他CRUD接口运行正常,但/api/employees接口会间歇性触发以下错误导致服务器崩溃:

res.status(500).json({
                ^
TypeError: Cannot read properties of undefined (reading 'status')

该请求仅在React应用的某个页面调用,无法稳定复现崩溃场景。需要解决两个问题:一是正确处理错误避免服务器崩溃,二是实现服务器崩溃自动重启机制。

解决方案

一、修复错误避免服务器崩溃

1. 给res参数添加空值校验

错误根源是catch块中res为undefined,可能是客户端主动断开请求(比如用户快速切换页面)导致res对象被释放。在withDB的catch块中先校验res的可用性:

const withDB = async (operations, res) => {
    try {
        const client = await MongoClient.connect('mongodb://localhost:27017', { useNewUrlParser: true });
        const db = client.db('database-name');
        await operations(db);
        client.close();
    } catch (error) {
        // 先判断res是否可用,避免调用undefined的方法
        if (res && typeof res.status === 'function') {
            res.status(500).json({ message: 'Error connecting to db', error: error.message });
        } else {
            // 无可用res时仅打印日志,防止崩溃
            console.error('Database error with no valid response object:', error);
        }
    }
}

2. 优化withDB调用逻辑,处理异步错误

当前withDB是异步函数,但调用时未加await,若operations内部抛错时res已失效,会引发异常。修改接口调用逻辑:

app.get('/api/employees', async (req, res) => {
    try {
        await withDB(async (db) => {        
            const tabInfo = await db.collection('users').find().toArray()
            // 响应前检查是否已发送过响应,避免重复响应报错
            if (!res.headersSent) {
                res.status(200).json(tabInfo);
            }
        }, res);
    } catch (error) {
        console.error('/api/employees接口错误:', error);
        if (!res.headersSent) {
            res.status(500).json({ message: '服务器内部错误' });
        }
    }
});

3. 改用MongoDB连接池(推荐)

每次请求创建新连接不仅效率低,还可能因连接未正确关闭引发资源泄漏。改用连接池复用连接:

// 全局初始化连接池,仅执行一次
let dbInstance;
const initMongoDB = async () => {
    const client = await MongoClient.connect('mongodb://localhost:27017', { 
        useNewUrlParser: true,
        poolSize: 10 // 设置连接池大小,按需调整
    });
    dbInstance = client.db('database-name');
    console.log('MongoDB连接池初始化完成');
};

// 初始化连接池
initMongoDB().catch(err => console.error('MongoDB初始化失败:', err));

// 修改withDB函数,无需每次创建新连接
const withDB = async (operations) => {
    if (!dbInstance) {
        throw new Error('MongoDB连接未初始化');
    }
    try {
        await operations(dbInstance);
    } catch (error) {
        throw error; // 将错误抛给上层接口处理
    }
}

// 接口调用示例
app.get('/api/employees', async (req, res) => {
    try {
        await withDB(async (db) => {        
            const tabInfo = await db.collection('users').find().toArray()
            if (!res.headersSent) {
                res.status(200).json(tabInfo);
            }
        });
    } catch (error) {
        console.error('/api/employees接口错误:', error);
        if (!res.headersSent) {
            res.status(500).json({ message: '服务器内部错误', error: error.message });
        }
    }
});

二、实现服务器崩溃自动重启

1. 使用PM2进程管理器

这是Node.js生产环境最常用的进程管理工具,能自动重启崩溃的服务,还支持日志监控、负载均衡等功能:

  • 安装:npm install pm2 -g
  • 启动服务:pm2 start app.js(假设你的入口文件是app.js)
  • 常用命令:
    • 查看服务状态:pm2 status
    • 查看实时日志:pm2 logs
    • 手动重启服务:pm2 restart app

2. 全局错误捕获兜底

在Express中添加全局错误处理中间件,捕获所有未处理的异常:

// 全局错误处理中间件,需放在所有路由定义之后
app.use((err, req, res, next) => {
    console.error('未捕获的接口错误:', err);
    if (!res.headersSent) {
        res.status(500).json({ message: '服务器内部错误' });
    }
});

// 捕获未处理的Promise拒绝
process.on('unhandledRejection', (reason, promise) => {
    console.error('未处理的Promise拒绝:', promise, '原因:', reason);
});

// 捕获未捕获的同步异常
process.on('uncaughtException', (error) => {
    console.error('未捕获的同步异常:', error);
    // 优雅退出,PM2会自动重启服务
    process.exit(1);
});

内容的提问来源于stack exchange,提问作者Nathan Field

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 17:24:29