本地服务监听 0.0.0.0:3000,curl localhost:3000 能通。Node 脚本里 fetch('http://localhost:3000') 报 ECONNREFUSED ::1:3000。第一次遇到会往复杂处想,其实漏掉的多是个小前提。
node -e "require('dns').lookup('localhost',{all:true},(e,a)=>console.log(a))"
server.listen(3000, '::');
Node 18 之后 localhost 解析优先返回 ::1,而服务只监听了 IPv4 的 0.0.0.0,IPv6 那一侧没有进程接收。curl 的解析顺序不同,所以看起来是好的。这一点先确认清楚,后面就不用反复试。
「localhost IPv6 解析」相关的验证我通常做两遍,改完一次、隔天再看一次,确认不是临时生效。把复现步骤压到三条以内,交给同事也能跑出来,才算真的定位清楚。
改动写完就同步给同事,下一个人踩同一个坑,成本还是你的。验证用真实数据走一遍,测试数据太干净,边界情况根本碰不到。
能分两步做的操作就别合成一步,中间看一眼返回值,出问题好判断。报错里带路径的,先去看那个路径在不在、权限对不对。
具体做法是,把请求地址换成 127.0.0.1 先排除解析因素;服务端监听 :: 或者同时监听两个协议栈;用 dns.lookup 的 family 参数强制 IPv4;容器里确认 IPv6 是否被禁用。
改完照着核一遍,dns.lookup 能看到 ::1 和 127.0.0.1 两条 fetch 请求正常返回 200。
