Node.js中用undici的ProxyAgent配合fetch无响应(Axios正常)
使用undici ProxyAgent配合fetch发起代理请求无响应,其他工具正常
问题描述
在Node.js项目中尝试用undici的ProxyAgent配合原生fetch发起带代理的GET请求,但请求既不resolve也不reject,代理代码里的第一个console.log完全没执行。但用Axios、http.get甚至Postman都能正常通过该代理请求。想知道是不是操作有误?undici的代理是否只支持特定类型的代理?
运行环境:Node.js LTS 20.17.0(也试过18.18.2)
相关代码
测试代码 index.js
import { ProxyAgent } from "undici"; const client = new ProxyAgent("http://127.0.0.1:8000"); fetch("http://ifconfig.io/ip", { dispatcher: client, method: "GET", }) .then((response) => { console.log("Got response"); if (!response.ok) { throw Error("response not ok"); } return response.text(); }) .then((data) => { console.log("Response Data = ", data); }) .catch((error) => { console.error("Error occurred:", error.message); });
本地代理服务 proxy.js(兼容Axios和Postman)
const http = require('http'); const httpProxy = require('http-proxy'); const url = require('url'); // 创建代理服务器 const proxy = httpProxy.createProxyServer({}); // 创建HTTP服务器并使用代理 const server = http.createServer((req, res) => { console.log("Original request URL:", req.url); const parsedUrl = url.parse(req.url, true); // 直接设置目标为后端服务 const target = `${parsedUrl.protocol}//${parsedUrl.host}`; // 将请求代理到目标服务器 proxy.web(req, res, { target }); // 记录代理请求 console.log(`Proxied request to: ${target}`); }); // 监听端口8000 const PORT = 8000; server.listen(PORT, () => { console.log(`动态代理服务器运行在 http://localhost:${PORT}`); });
package.json
{ "name": "nodejs-proj", "version": "1.0.0", "description": "", "main": "index.js", "type":"module", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "axios": "^1.7.4", "http-proxy": "^1.18.1", "http-proxy-agent": "^7.0.2", "undici": "^6.19.8" } }
Axios正常工作示例 indexAxios.js
import axios from "axios"; axios.get('https://ifconfig.io/ip', { proxy: { protocol: "http", host: '127.0.0.1', port: 8000, } }) .then(response => { console.log('Response data:', response.data); }) .catch(error => { console.error('Error occurred:', error.message); });
其他测试过的代理实现(同样兼容Axios但不兼容undici fetch)
基于request模块的代理
import http from "http"; import request from "request"; const PORT = 8000; http.createServer(function (req, res) { console.log(`Incoming request to ${req.url}`); req.pipe(request(req.url)).pipe(res); }).listen(PORT);
基于proxy模块的代理
import * as http from 'http'; import { createProxy } from 'proxy'; const server = createProxy(http.createServer()); // 记录每个入站请求 server.on('request', (req, res) => { console.log(`Incoming request: ${req.method} ${req.url}`); }); server.listen(3128, () => { var port = server.address().port; console.log('HTTP(s) proxy server listening on port %d', port); });
问题原因与解决方法
核心问题
undici的ProxyAgent遵循HTTP代理标准,会直接向代理服务器发送完整的目标URL作为请求路径(比如GET http://ifconfig.io/ip HTTP/1.1),而你的本地代理服务器只处理了Axios等工具发送的相对路径请求,无法识别这种绝对URI格式的请求,导致请求卡住。
具体修复方案
方案1:修改本地代理服务器,支持绝对路径请求
调整proxy.js的逻辑,兼容undici发送的绝对URI请求:
const http = require('http'); const httpProxy = require('http-proxy'); const url = require('url'); const proxy = httpProxy.createProxyServer({}); const server = http.createServer((req, res) => { console.log("Original request URL:", req.url); let target; // 处理undici发送的绝对路径URL if (req.url.startsWith('http://') || req.url.startsWith('https://')) { const parsedUrl = url.parse(req.url); target = `${parsedUrl.protocol}//${parsedUrl.host}`; // 将请求URL改为相对路径,适配http-proxy的web方法 req.url = parsedUrl.path; } else { // 兼容Axios等工具的相对路径请求 const parsedUrl = url.parse(req.url, true); target = `${parsedUrl.protocol}//${parsedUrl.host}`; } // 添加错误处理,避免请求无响应 proxy.web(req, res, { target }, (err) => { console.error('Proxy error:', err); res.writeHead(500); res.end('Proxy error'); }); console.log(`Proxied request to: ${target}`); }); const PORT = 8000; server.listen(PORT, () => { console.log(`动态代理服务器运行在 http://localhost:${PORT}`); });
方案2:使用undici自带fetch的proxy选项
无需手动创建ProxyAgent,直接用undici的fetch内置的proxy配置,内部会自动处理正确的请求格式:
import { fetch } from "undici"; fetch("http://ifconfig.io/ip", { method: "GET", proxy: "http://127.0.0.1:8000" }) .then((response) => { console.log("Got response"); if (!response.ok) { throw Error("response not ok"); } return response.text(); }) .then((data) => { console.log("Response Data = ", data); }) .catch((error) => { console.error("Error occurred:", error.message); });
额外说明
- undici的代理实现严格遵循RFC 7230标准,要求代理服务器支持绝对URI请求;如果是HTTPS目标,还需要支持CONNECT方法建立隧道,
http-proxy默认支持该方法,自定义代理则需要手动处理。 - Axios默认采用传统的相对路径代理方式,和undici的标准行为存在差异,这也是为什么前者能正常工作的原因。
内容的提问来源于stack exchange,提问作者Manolo
相关产品推荐
相关产品推荐

