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

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这类由代理服务器自己管理的头,会引发响应体长度计算错误

修复步骤

  1. 替换流处理方式:使用Node.js原生流API将Web ReadableStream转换为Node.js可读流,再pipe到响应对象
  2. 优化响应头转发逻辑:跳过会引发冲突的响应头
  3. 启用自定义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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 06:13:14