Next.js 13 App Router用http-proxy报错:req.on is not a function
解决Next.js 13 App Router中http-proxy代理外部API的问题
问题背景
在Next.js 13 App Router中尝试用路由处理器实现外部API代理,使用http-proxy时出现错误req.on is not a function。原因是http-proxy期望接收Node.js原生的IncomingMessage类型请求,而App Router的路由处理器传入的是NextRequest,两者不兼容。同时需要在服务端为请求追加accessToken,无法使用Next.js重写方案。
用户原代码:
import { authOptions } from "@/modules/auth/utils/auth"; import httpProxy from "http-proxy"; import { getServerSession } from "next-auth"; import { NextRequest, NextResponse } from "next/server"; const ProxyHandler = async (req: NextRequest, res: NextResponse) => { const session = await getServerSession(authOptions); return new Promise((resolve, reject) => { const proxy: httpProxy = httpProxy.createProxy(); proxy.once("proxyRes", resolve).once("error", reject).web(req, res, { changeOrigin: true, target: process.env.NEXT_PUBLIC_API_URL, }); }); }; export const config = { api: { bodyParser: false, externalResolver: true, }, }; export { ProxyHandler as GET, ProxyHandler as POST, ProxyHandler as PUT, ProxyHandler as DELETE, ProxyHandler as PATCH, };
可行解决方案
方案一:将NextRequest转换为Node.js原生IncomingMessage适配http-proxy
通过Node.js的http模块,把NextRequest转换为IncomingMessage,同时处理响应的转换,还可在代理配置中追加accessToken到请求头:
import { authOptions } from "@/modules/auth/utils/auth"; import httpProxy from "http-proxy"; import { getServerSession } from "next-auth"; import { NextRequest, NextResponse } from "next/server"; import { IncomingMessage, ServerResponse } from "node:http"; import { Readable } from "node:stream"; const ProxyHandler = async (req: NextRequest) => { const session = await getServerSession(authOptions); if (!session?.user?.accessToken) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } return new Promise<NextResponse>((resolve, reject) => { const proxy = httpProxy.createProxy(); // 将NextRequest转换为IncomingMessage const nodeReq = new IncomingMessage(Readable.from(req.body || [])); nodeReq.method = req.method; nodeReq.url = req.nextUrl.pathname + req.nextUrl.search; nodeReq.headers = { ...req.headers }; // 追加accessToken到请求头 nodeReq.headers["authorization"] = `Bearer ${session.user.accessToken}`; // 创建临时ServerResponse来捕获代理响应 let responseBody = Buffer.from(""); const nodeRes = new ServerResponse(nodeReq); nodeRes.write = (chunk) => { responseBody = Buffer.concat([responseBody, chunk]); return true; }; nodeRes.end = () => { const response = new NextResponse(responseBody, { status: nodeRes.statusCode, headers: nodeRes.getHeaders() as HeadersInit, }); resolve(response); }; proxy.web(nodeReq, nodeRes, { changeOrigin: true, target: process.env.NEXT_PUBLIC_API_URL, }); proxy.once("error", (err) => { reject(new NextResponse(JSON.stringify({ error: err.message }), { status: 500 })); }); }); }; export { ProxyHandler as GET, ProxyHandler as POST, ProxyHandler as PUT, ProxyHandler as DELETE, ProxyHandler as PATCH };
方案二:使用Fetch直接转发请求(更适配App Router)
放弃http-proxy,直接用fetch转发请求,这种方式贴合Next.js 13 App Router的设计,无需类型转换,可轻松追加accessToken:
import { authOptions } from "@/modules/auth/utils/auth"; import { getServerSession } from "next-auth"; import { NextRequest, NextResponse } from "next/server"; const proxyHandler = async (req: NextRequest) => { const session = await getServerSession(authOptions); if (!session?.user?.accessToken) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } const targetUrl = new URL(req.nextUrl.pathname + req.nextUrl.search, process.env.NEXT_PUBLIC_API_URL); // 构建请求选项,复制原请求的方法、body、headers,追加accessToken const requestOptions: RequestInit = { method: req.method, headers: new Headers(req.headers), body: req.method !== "GET" && req.method !== "HEAD" ? await req.text() : undefined, redirect: "manual", }; // 替换或添加Authorization头 requestOptions.headers.set("authorization", `Bearer ${session.user.accessToken}`); // 移除Next.js特定的头,避免干扰外部API requestOptions.headers.delete("host"); try { const response = await fetch(targetUrl.toString(), requestOptions); return new NextResponse(response.body, { status: response.status, statusText: response.statusText, headers: response.headers, }); } catch (error) { return NextResponse.json({ error: "Proxy request failed" }, { status: 500 }); } }; export { proxyHandler as GET, proxyHandler as POST, proxyHandler as PUT, proxyHandler as DELETE, proxyHandler as PATCH };
方案说明
- 方案二更推荐,完全适配App Router的路由处理器模型,代码简洁,无需复杂类型转换,调试维护更方便。
- 两种方案均实现了服务端追加
accessToken的需求,确保令牌不会暴露给客户端。
内容的提问来源于stack exchange,提问作者MajdNaji
相关产品推荐
相关产品推荐

