如何让axios读取超出Content-length长度的完整HTTP响应体
原因说明
你猜测的逻辑是正确的,响应体截断是Node.js内置http/https模块的默认行为:只要响应头携带Content-Length字段,模块只会读取对应字节数就触发流的end事件,超出部分会被直接丢弃,和上层axios逻辑无关。
解决方案
关闭Content-Length严格校验即可,核心是给底层HTTP请求传递strictContentLength: false参数,按你的技术栈给出两种实现方式:
方案1:继续使用axios,自定义适配器透传参数
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'; import * as http from 'http'; import * as https from 'https'; import { Stream } from 'stream'; // 自定义适配,核心是新增strictContentLength参数关闭校验 const customAdapter = (config: AxiosRequestConfig): Promise<AxiosResponse> => { return new Promise((resolve, reject) => { const transport = config.url?.startsWith('https') ? https : http; const req = transport.request({ ...config, strictContentLength: false, }, (res) => { resolve({ data: res, status: res.statusCode, statusText: res.statusMessage, headers: res.headers, config, request: req, }); }); req.on('error', reject); if (config.data) req.write(config.data); req.end(); }); }; // 你的原有axios配置新增adapter字段即可 const config: AxiosRequestConfig = { method: 'GET', url: 'https://替换为你的请求地址', headers: { Cookie: 'VerySecretCookie', }, responseType: 'stream', maxRedirects: 0, decompress: false, timeout: 60000, adapter: customAdapter, rejectUnauthorized: false, // 对应curl的-k参数,需要忽略证书校验可保留,不需要可删除 }; // 原有流转换逻辑可以复用,仅建议新增toString避免Buffer拼接异常 private async streamToString(stream: Stream): Promise<string> { return new Promise((res, rej) => { let data = ''; stream.on('end', () => res(data)); stream.on('error', (err) => rej(err)); stream.on('data', (chunk) => data += chunk.toString()); }); } // 调用逻辑不变 const response = await axios(config); const fullData = await this.streamToString(response.data);
方案2:无需axios,直接用原生HTTP模块实现
如果没有强依赖axios的其他能力,直接用原生模块代码更精简:
import * as https from 'https'; import { Stream } from 'stream'; private async streamToString(stream: Stream): Promise<string> { return new Promise((res, rej) => { let data = ''; stream.on('end', () => res(data)); stream.on('error', (err) => rej(err)); stream.on('data', (chunk) => data += chunk.toString()); }); } const fullData = await new Promise((resolve, reject) => { const req = https.request({ hostname: '替换为你的域名', path: '替换为你的请求路径', method: 'GET', headers: { Cookie: 'VerySecretCookie', }, rejectUnauthorized: false, strictContentLength: false, }, async (res) => { try { const data = await streamToString(res); resolve(data); } catch (e) { reject(e); } }); req.on('error', reject); req.end(); });
注意事项
strictContentLength参数支持Node.js 12.19.0、14.5.0及以上版本,如果你使用的Node版本低于要求,可在收到响应头时手动删除res.headers['content-length'],也能达到相同效果。- 该方案仅适配你提到的未启用HTTP Keep-Alive的场景,如果开启了Keep-Alive,连接不会在单次请求结束后断开,可能会读到后续请求的响应内容,导致数据异常。
内容的提问来源于stack exchange,提问作者YSK
相关产品推荐
相关产品推荐

