Express代理服务器CURL请求正常,Fetch请求报解码失败
问题排查与修复方案
核心问题原因
你的代理服务器在转发响应时,错误地使用了浏览器标准的WritableStream来处理Node.js的HTTP响应流。Node.js的http.ServerResponse(即Express的res对象)基于Node.js原生流实现,和浏览器的Web Stream体系不兼容,直接通过pipeTo传输数据会导致数据传输不完整、响应头处理异常,最终触发前端Fetch的JSON解码失败——而curl对响应的容错性更高,所以能正常解析。
另外还有两个潜在问题:
- 你定义了
corsOption但未实际启用,可能导致跨域头配置不符合预期 - 直接转发所有响应头,包括
Transfer-Encoding、Content-Length这类由代理服务器自己管理的头,会引发响应体长度计算错误
修复步骤
- 替换流处理方式:使用Node.js原生流API将Web ReadableStream转换为Node.js可读流,再pipe到响应对象
- 优化响应头转发逻辑:跳过会引发冲突的响应头
- 启用自定义CORS配置:确保跨域请求的头正确设置
修复后的完整代码
import express, { Express, Request, Response } from 'express'; import dotenv from 'dotenv'; import { Readable } from 'stream'; // 引入Node.js流模块 dotenv.config(); const app: Express = express(); const port = process.env.PORT || 3000; const apikey = process.env.API_KEY; // 建议从环境变量读取 const pathsToProxy = '/api/'; const MONGODB_URL = process.env.MONGODB_URL; // 建议从环境变量读取 const corsOption = { credentials: true, origin: ['http://localhost:8080', 'http://localhost:3000'], }; // 启用自定义CORS配置 app.use(cors(corsOption)); // 使用Express内置的json解析中间件(替代bodyParser.json()) app.use(express.json()); app.all('/api/*', async (req: Request, res: Response) => { if (!MONGODB_URL || !apikey) { return res.status(500).send('Missing configuration'); } const url = req.url.replace(pathsToProxy, MONGODB_URL); const headers = new Headers(); headers.set('Content-Type', req.get('content-type') || 'application/json'); headers.set('Accept', req.get('accept') || 'application/json'); headers.set('apiKey', apikey); const mongoReq = { method: req.method, headers: headers, body: ['PUT', 'POST'].includes(req.method) ? JSON.stringify(req.body) : undefined, }; try { const mongoRes = await fetch(url, mongoReq); // 设置响应状态码 res.status(mongoRes.status); // 转发响应头,跳过会冲突的头 mongoRes.headers.forEach((value, name) => { const lowerName = name.toLowerCase(); if (!['transfer-encoding', 'content-length', 'connection'].includes(lowerName)) { res.setHeader(name, value); } }); // 将Web ReadableStream转换为Node.js可读流并pipe到响应 if (mongoRes.body) { const nodeReadableStream = Readable.fromWeb(mongoRes.body); nodeReadableStream.pipe(res); } else { res.end(); } } catch (e) { console.error('Proxy error:', e); res.status(500).send(`Proxy error: ${String(e)}`); } }); app.listen(port, () => { console.log(`[server]: Server is running at http://localhost:${port}`); });
额外验证建议
- 前端调用时,在
catch中打印完整错误信息,确认是否是JSON格式错误:.catch((e) => { console.error('Fetch error:', e); console.error('Error message:', e.message); }); - 使用浏览器开发者工具的网络面板查看代理返回的响应体,确认是否有乱码、截断或格式错误
内容的提问来源于stack exchange,提问作者Alexis Purslane
相关产品推荐
相关产品推荐

