Heroku部署后axios重定向请求无法获取响应如何解决
问题原因
- 进程崩溃直接原因:接口为async函数但未做全局异常捕获,只要axios请求、数据库操作抛出任何错误(比如目标站拦截、TLS握手失败、重定向逻辑异常),就会触发未捕获的Promise rejection,直接导致Node进程以状态码1退出,对应Heroku日志里的H13(连接无响应关闭)报错。
- 拿不到重定向后内容的原因:Heroku环境下发的出站请求默认带
axios/xxx的User-Agent标识,很容易被MaxPreps的反爬策略拦截;同时你没有显式配置axios的重定向次数、超时参数,部分Heroku dyno的网络环境下默认重定向逻辑会直接返回套接字对象,而非最终的HTML响应。 - 隐性逻辑bug:你用全局
displayGames数组存单次请求的临时数据,并发访问时会出现不同请求的数据串扰;另外数据库的findOne/save异步操作没有做等待处理,就直接返回响应、清空数组,很容易触发写入异常、偶发报错。
修复后可直接运行的代码
先在文件顶部按需引入依赖:
const https = require('https') // 用于TLS配置,出现握手报错时使用
接口代码替换为如下内容:
server.get('/displayGames', async (req, res) => { // 临时数据改为接口内部局部变量,彻底避免并发污染 const displayGames = [] try { const response = await axios.get('https://www.maxpreps.com/list/schedules_scores_redirect.aspx?gendersport=boys,football', { maxRedirects: 10, // 显式配置最多10次重定向,覆盖默认配置的异常问题 timeout: 15000, // 设置15秒超时,避免请求长期挂死 headers: { // 模拟真实浏览器请求头,绕过目标站基础反爬 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language': 'en-US,en;q=0.5' }, // 如果仍报TLS握手错误,放开下面这行注释 // httpsAgent: new https.Agent({ minVersion: 'TLSv1.2' }) }) const html = response.data const $ = cheerio.load(html) const dbTasks = [] $('.contest-box-item').find('.teams').each(function() { const winner = $(this).find('.winner').find('.name').text().trim() const loser = $(this).find('[data-result="3"]').find('.name').text().trim() const winningScore = $(this).find('.winner').find('.score').text().trim() const losingScore = $(this).find('[data-result="3"]').find('.score').text().trim() const winningTeamLogo = $(this).find('.winner').find('.image').attr('data-lazy-image') const losingTeamLogo = $(this).find('[data-result="3"]').find('.image').attr('data-lazy-image') if (winner && loser) { displayGames.push({winner, loser, winningScore, losingScore, winningTeamLogo, losingTeamLogo}) // 数据库操作统一收集,单独捕获错误不影响主流程 const task = displayGameModel.findOne({winnerName: winner, loserName: loser}) .then(existRecord => { if (!existRecord) { return new displayGameModel({ winnerName: winner, winnerScore: winningScore, winnerLogo: winningTeamLogo, loserName: loser, loserScore: losingScore, loserLogo: losingTeamLogo }).save() } }) .catch(dbErr => console.error('单条数据写入失败:', dbErr)) dbTasks.push(task) } }) // 等待所有数据库操作执行完成,避免异步进程挂死 await Promise.allSettled(dbTasks) res.json(displayGames) } catch (reqErr) { console.error('接口处理异常:', reqErr) // 异常时返回标准错误响应,不中断整个Node进程 res.status(500).json({ error: '数据获取失败,请稍后重试' }) } })
部署注意项
- 部署前确认Heroku的环境变量里数据库连接地址配置正确,数据库IP白名单已放开Heroku dyno的出站IP段。
- 如果后续出现目标站返回403/Cloudflare拦截,不要频繁请求,可适当增加请求缓存逻辑,避免IP被封禁。
- 所有Node.js服务的async路由必须加异常捕获,未捕获的Promise rejection在新版Node.js里会直接终止进程,是线上服务崩溃的高频诱因。
内容的提问来源于stack exchange,提问作者Daniel Brannon
相关产品推荐
相关产品推荐

