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

从Next.js API路由请求带Oak中间件的Deno服务器遇连接拒绝问题

问题背景与现象

我尝试从Next.js的API路由请求Deno服务器,为简化测试,使用Oak中间件编写了如下Deno服务器:

import { Application } from 'https://deno.land/x/oak/mod.ts'
import { oakCors } from 'https://deno.land/x/cors/mod.ts'

const app = new Application()

app.use(
  oakCors({
    origin: 'http://localhost:3000',
    credentials: true
  })
)

app.use((ctx) => {
  ctx.response.body = 'Hello World!'
})

await app.listen({ port: 3001 });

已配置oakCors允许来自Next.js应用(运行在http://localhost:3000)的跨域请求。接着在Next.js的/pages/api/test.js中实现对Deno服务器的请求:

export default async function handler(req, res) {
  try {
    const data = await fetch('http://localhost:3001')
    console.log(data)
  } catch (e) {
    console.log(e)
  }

  res.json({ done: true })
}

页面渲染两个按钮用于测试:

<button onClick={() => fetch('http://localhost:3001')}>direct fetch</button>
<button onClick={() => fetch('api/test')}>fetch by api routes</button>

测试现象:

  • 点击第一个按钮直接请求Deno服务器,请求正常返回
  • 点击第二个按钮通过Next.js API路由间接请求,Next.js服务器控制台出现连接拒绝错误:
error - unhandledRejection: TypeError: fetch failed
    at Object.fetch (node:internal/deps/undici/undici:14062:11)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
  cause: Error: connect ECONNREFUSED 127.0.0.1:3001
      at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1487:16) {
    errno: -111,
    code: 'ECONNREFUSED',
    syscall: 'connect',
    address: '127.0.0.1',
    port: 3001
  }
}

补充说明:若为http://localhost:3001开启ngrok隧道,两种请求方式均能通过生成的隧道URL正常工作。


原因分析

核心差异在于请求的执行环境:

  1. 直接点击按钮的请求是从浏览器客户端发起的,此时localhost:3001指向本地运行的Deno服务器,请求能正常到达。
  2. 通过API路由的请求是在**Next.js服务端(Node.js进程)**执行的:
    • 如果Next.js运行在容器化环境(如Docker)中,localhost对于容器内的进程来说,指向的是容器自身而非宿主机的Deno服务器,因此会出现连接拒绝。
    • 即使是本地开发,部分情况下Next.js服务端进程的网络命名空间与本地主机不共享,也会导致localhost:3001无法解析到Deno服务器。
  3. ngrok隧道是公网可访问的地址,客户端和服务端都能正确解析到Deno服务器的公网入口,因此两种方式都能正常工作。

修复方案

1. 使用本地主机IP替代localhost

将API路由中的请求地址从http://localhost:3001替换为你的本地主机IP(如192.168.x.x),确保服务端能直接定位到运行Deno服务器的机器:

export default async function handler(req, res) {
  try {
    // 替换为你的实际本地IP
    const data = await fetch('http://192.168.1.100:3001')
    console.log(data)
  } catch (e) {
    console.log(e)
  }

  res.json({ done: true })
}

2. 容器化场景使用主机别名

如果使用Docker运行Next.js,可使用Docker默认提供的host.docker.internal作为宿主机别名,替换localhost:

export default async function handler(req, res) {
  try {
    const data = await fetch('http://host.docker.internal:3001')
    console.log(data)
  } catch (e) {
    console.log(e)
  }

  res.json({ done: true })
}

3. 环境变量统一管理

将Deno服务器地址存入环境变量,避免硬编码,适配不同运行环境:

  1. 在Next.js项目根目录创建.env.local文件:
DENO_SERVER_URL=http://192.168.1.100:3001
  1. 修改API路由代码读取环境变量:
export default async function handler(req, res) {
  try {
    const data = await fetch(process.env.DENO_SERVER_URL)
    console.log(data)
  } catch (e) {
    console.log(e)
  }

  res.json({ done: true })
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 19:35:16