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

Express服务调用res.json崩溃及Heroku部署异常排查

问题根因

代码存在3个核心问题,直接导致本地运行崩溃、Heroku部署后服务被强制终止:

  • 异步逻辑处理错误:Array.prototype.forEach 不会等待内部异步操作执行完成,遍历还未结束、数据库写入操作仍在排队时就调用res.json返回响应,后续异步任务抛出的未捕获异常会直接终止Node进程
  • 接口异常未捕获:/:slug 路由的OpenseaScraper.basicInfo调用没有包裹异常处理,OpenSea接口返回404时,依赖内置axios抛出的错误没有被拦截,直接触发进程崩溃,日志里请求favicon.ico返回404就是该逻辑触发的连锁问题
  • 部署配置错误:Heroku环境如果使用nodemon作为生产启动命令,会因为生产环境无文件变更触发监听异常,直接导致进程崩溃退出,和日志里app crashed - waiting for file changes before starting的记录完全对应

你观察到的「传参给res.json就崩溃、移除res.json本地正常Heroku报错」现象本质是:调用res.json后请求生命周期正常结束,后续未捕获的异步错误会直接触发进程退出;移除res.json后请求一直处于挂起状态,本地不会触发进程终止逻辑,但Heroku有严格的请求超时机制,挂起请求超过超时阈值会直接强制杀进程,返回H13、H21类错误码。

修复方案

1. 修正根路由异步逻辑

不要用forEach处理异步遍历,改用for...of循环等待所有数据库操作完成后再返回响应,同时补全数据库操作的异常捕获,避免单条记录写入失败打崩整个接口。
替换原根路由代码为:

server.get('/', async (req, res) => {
    try {
        const NFTs = []
        const ranking = await OpenseaScraper.rankings()
        
        // 用for...of替代forEach,正确等待异步操作执行
        for (const item of ranking) {
            const slug = item.slug
            const name = item.name
            const logoUrl = item.logo
            NFTs.push({slug, name, logoUrl})
            // 单条数据库操作加异常捕获
            try {
                const existNFT = await NFT.findOne({ "slug": slug })
                if (!existNFT) {
                    const newNFT = new NFT({slug, name, logoUrl})
                    await newNFT.save()
                }
            } catch(dbErr) {
                console.error('NFT记录写入失败:', dbErr)
            }
        }
    
        // 所有逻辑执行完成后再返回响应
        res.json({ list: NFTs })
    } catch(err) {
        console.error('排名接口报错:', err)
        // 异常场景必须返回响应,避免请求挂起
        res.status(500).json({ msg: '获取NFT排名失败' })
    }
})

2. 补全slug路由的异常捕获

原slug路由没有任何异常处理逻辑,OpenSea接口404时会直接抛错打崩进程,替换为以下代码:

server.get('/:slug', async (req, res) => {
    try {
        const basicInfo = await OpenseaScraper.basicInfo(req.params.slug)
        const floorPrice = basicInfo.floorPrice
        // 先执行数据库更新,再返回响应
        await NFT.findOneAndUpdate(
            { "slug": req.params.slug }, 
            {$set: {"floorPrice": floorPrice}}
        )
        res.json({
            name: req.params.slug.replaceAll('-', ' '),
            floorPrice: floorPrice
        })
    } catch(err) {
        console.error('NFT详情查询失败:', err)
        res.status(500).json({ msg: '获取NFT详情失败' })
    }
})

3. 修正数据库连接逻辑

原mongoose连接写法没有捕获连接异常,且存在拼写错误,替换为以下写法:

mongoose.connect(process.env.MONGODB_URL)
  .then(() => console.log('This application has been connected to the Mongo DB!'))
  .catch(err => console.error('数据库连接失败:', err))

代码中定义的无用常量const PORT = 8001可以直接删除,现有监听逻辑已经正确优先读取Heroku分配的process.env.PORT变量,不存在端口适配问题。

4. 调整Heroku部署配置

  • 生产环境禁止用nodemon启动服务,修改package.json的脚本配置,区分开发和生产启动命令:
    {
      "scripts": {
        "start": "node 你的入口文件名.js",
        "dev": "nodemon 你的入口文件名.js"
      }
    }
    
  • 确认Heroku后台配置的环境变量中已经正确填写MONGODB_URL,避免数据库连接失败导致进程退出。

内容的提问来源于stack exchange,提问作者Daniel Brannon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 16:31:06