Docker Compose环境下Node+Axios调用Python服务遇ENOTFOUND错误
看起来你遇到的问题根源在于Axios是在jsdom模拟的浏览器环境下运行,而非原生Node.js环境,这导致DNS解析出现异常——虽然原生Node的dns.lookup能正确解析app,但jsdom的XMLHttpRequest实现无法正确处理Docker内部的服务域名。
先澄清一个误区
你提到ping app:5000有响应,但实际上ping是基于ICMP协议的,它不识别端口号,输入app:5000时,ping会把整个字符串当作主机名去解析(这也是为什么dns.lookup('app:5000')会失败),所以这个测试并不能证明端口可达,真正有效的测试是你之前用的curl http://app:5000/status。
核心问题分析
从错误栈里的jsdom/lib/jsdom/virtual-console.js和xhr-utils.js可以看出,你的Node代码是在jsdom环境中执行的(比如用Jest做测试,或者依赖jsdom模拟浏览器环境)。jsdom自带的XMLHttpRequest实现和原生Node的http模块在处理内部DNS时存在差异,导致它无法正确解析Docker Compose自动创建的服务域名app。
解决方案
方案1:强制Axios使用Node原生HTTP适配器
让Axios绕过jsdom的XHR,直接使用Node.js的http模块发送请求,这样就能正常利用Docker的DNS解析了。在你的代码中添加以下配置:
import axios from 'axios'; // 引入Axios的Node原生适配器 const httpAdapter = require('axios/lib/adapters/http'); // 配置Axios使用原生适配器 axios.defaults.adapter = httpAdapter; // 之后正常发送请求 this.endpointUrl = 'http://app:5000'; const options: AxiosRequestConfig = { method: 'POST', url: `${this.endpointUrl}/session/get`, data: { 'client': 'alexa' }, responseType: 'json' }; axios(options) .then((response: AxiosResponse<any>) => { console.log(`[SessionService][getSession]: "/session/get" responseData = ${JSON.stringify(response.data)}`); }) .catch(err => { console.error(`[SessionService][getSession]: error = ${err}`); });
方案2:调整jsdom配置(如果必须保留浏览器环境)
如果你需要继续在jsdom环境中运行代码,可以在创建jsdom实例时,启用资源访问并允许外部网络请求:
const { JSDOM } = require('jsdom'); const dom = new JSDOM('<!DOCTYPE html>', { // 允许加载外部资源 resources: 'usable', // 配置虚拟控制台,可选 virtualConsole: new JSDOM.VirtualConsole().sendTo(console), // 如果需要模拟完整浏览器环境,可添加其他选项 }); // 确保全局对象指向jsdom的window global.window = dom.window; global.document = dom.window.document;
这样配置后,jsdom的XHR应该能正确解析Docker内部的服务域名了。
验证
修改后,你可以再用Node的dns.lookup('app')确认解析正常,然后运行代码测试POST请求是否能成功发送。
内容的提问来源于stack exchange,提问作者Corey Cole

