Node.js 原生模块怎么发 HTTP 请求?
Node.js 本身自带 http 和 https 模块,不用额外装依赖,适合做基础请求,先从最常见的 GET、POST 说起。
GET 请求:一行代码发起,重点处理响应
用 http.get() 能快速发 GET 请求,它自动帮你处理请求方法、路径这些细节,比如调个公开 API 拿数据:
const http = require('http');
http.get('http://api.example.com/data', (res) => {
let data = '';
// 分段接收响应体(大响应时会分片)
res.on('data', (chunk) => { data += chunk; });
// 响应结束后处理数据
res.on('end', () => {
try {
const result = JSON.parse(data);
console.log('拿到数据:', result);
} catch (err) {
console.error('解析失败:', err);
}
});
// 监听网络错误(比如连不上服务器)
res.on('error', (err) => {
console.error('请求出错:', err);
});
}).on('error', (err) => { // 请求本身的错误(如DNS解析失败)
console.error('发起请求失败:', err);
});这里要注意,响应是“流”形式,得用 data 事件把分片拼起来,end 事件里处理完整数据。
POST 请求:手动构造请求头和请求体
POST 需要自己设置请求方法、请求头(Content-Type),还要把请求体写进去,举个发 JSON 数据的例子:
const http = require('http');
const postData = JSON.stringify({ name: '小明', age: 18 });
const options = {
hostname: 'api.example.com',
path: '/user',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': postData.length // 告诉服务端body长度
}
};
const req = http.request(options, (res) => {
// 同样用data和end事件收响应
let data = '';
res.on('data', (chunk) => { data += chunk; });
res.on('end', () => {
console.log('POST 响应:', data);
});
});
req.on('error', (err) => {
console.error('POST 出错:', err);
});
// 把请求体写进请求
req.write(postData);
req.end(); // 一定要end,否则请求发不出去原生 POST 步骤多些:配 options、建请求对象、写 body、监听响应和错误,要是发表单(application/x-www-form-urlencoded),原理类似,只是 postData 改成 name=小明&age=18 这种格式,再调整 Content-Type 就行。
原生模块的优缺点
优点是“零依赖”,项目不想装第三方库时很省心;缺点也明显——代码繁琐(比如处理 JSON 解析、错误分支),复杂场景(如拦截请求、自动重试)得自己写逻辑,所以简单请求用原生,复杂需求更适合第三方库。
常用第三方库有啥优势?怎么选?
Node.js 生态里第三方 HTTP 库一大堆,核心是帮你简化代码、处理重复逻辑,挑几个最火的讲讲:
axios:前后端通吃的“万金油”
前端开发者很熟,Node 里也能装(npm i axios),它基于 Promise,语法简洁,还支持请求拦截、响应拦截、自动转换 JSON,比如发 GET:
const axios = require('axios');
axios.get('http://api.example.com/data')
.then(res => console.log(res.data))
.catch(err => console.error('请求失败:', err));POST 更简单,直接传数据:
axios.post('http://api.example.com/user', { name: '小明', age: 18 })
.then(res => console.log(res.data))
.catch(err => { /* 处理错误 */ });优势是“前后端一致”,团队技术栈统一时特方便;拦截器能统一加 token、处理错误(401 自动跳登录),但体积稍大,纯 Node 项目追求极致轻量的话,可能选更小巧的库。
got:轻量又现代的“新生代选手”
安装 npm i got,API 设计很“Node 风格”,支持流、Promise、TypeScript,还内置重试、超时、cookie 管理,比如发请求并流式处理大文件:
const got = require('got');
// 把响应直接存到本地文件(流处理,不占内存)
got.stream('https://example.com/big-file.zip').pipe(fs.createWriteStream('file.zip'));普通 JSON 请求更简洁:
(async () => {
const res = await got.get('http://api.example.com/data', { responseType: 'json' });
console.log(res.body); // 自动解析成对象
})();适合 Node 后端项目,尤其是要处理大文件、需要高可配置性的场景,文档也写得特清楚。
superagent:链式调用写着爽
安装 npm i superagent,语法像写句子一样流畅:
const superagent = require('superagent');
superagent
.get('http://api.example.com/data')
.query({ page: 1, size: 10 }) // 拼查询参数
.set('Authorization', 'Bearer xxx') // 设请求头
.end((err, res) => { // 回调风格,也支持Promise
if (err) return console.error(err);
console.log(res.body);
});链式调用对新手友好,一眼能看懂每步做啥,处理文件上传更方便:
superagent
.post('http://api.example.com/upload')
.attach('file', './avatar.jpg') // 自动转multipart/form-data
.end((err, res) => { /* ... */ });适合快速写 Demo 或小项目,代码可读性拉满。
node-fetch:前端开发者无缝迁移
如果你习惯浏览器里的 fetch,装 npm i node-fetch 就能在 Node 里用同款 API:
const fetch = require('node-fetch');
fetch('http://api.example.com/data')
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err));优点是学习成本低,前端同学能快速上手;但功能相对基础,复杂需求(如重试)得自己加逻辑或装插件。
选库思路:
简单请求用 axios 或 superagent 写着快;处理大文件、需要高级配置选 got;想和前端代码复用选 axios 或 node-fetch,要是项目极轻量,原生模块也够。
不同场景下怎么选工具?
光知道库还不够,得看场景挑方案,举几个典型场景:
批量请求+并发控制
比如要调 100 个接口,全并发容易把服务器打崩,得限制同时请求数,用 axios + Promise.all + 分组:
const axios = require('axios');
const urls = ['url1', 'url2', ..., 'url100']; // 100个地址
const batchSize = 10; // 每次发10个
const results = [];
async function batchRequest() {
for (let i = 0; i < urls.length; i += batchSize) {
const batch = urls.slice(i, i + batchSize);
const requests = batch.map(url => axios.get(url));
const resList = await Promise.all(requests);
results.push(...resList.map(r => r.data));
}
console.log('所有结果:', results);
}
batchRequest();要是用 got,可以用 got.pipeline 做流处理,更高效处理大量请求。
文件上传(multipart/form-data)
原生模块得自己拼请求头和边界,特麻烦,用 superagent 的 .attach() 一步到位:
const superagent = require('superagent');
superagent
.post('https://api.example.com/upload')
.attach('avatar', './avatar.jpg') // 文件名和路径
.attach('doc', './report.pdf') // 多文件上传
.end((err, res) => {
if (err) return console.error(err);
console.log('上传结果:', res.body);
});axios 也能做,不过得装 form-data 库配合:
const axios = require('axios');
const FormData = require('form-data');
const form = new FormData();
form.append('avatar', fs.createReadStream('./avatar.jpg'));
axios.post('https://api.example.com/upload', form, {
headers: form.getHeaders() // 自动设Content-Type和边界
}).then(res => console.log(res.data));处理大响应(避免内存爆炸)
比如下载大文件,用原生 http 的流管道:
const http = require('http');
const fs = require('fs');
const file = fs.createWriteStream('large-file.zip');
http.get('http://example.com/large-file.zip', (res) => {
res.pipe(file); // 响应流直接写到文件,不用存内存
file.on('finish', () => {
file.close();
console.log('下载完成');
});
}).on('error', (err) => {
fs.unlink('large-file.zip'); // 下载失败删临时文件
console.error('下载出错:', err);
});用 got 更简单,开流选项就行:
const got = require('got');
got.stream('http://example.com/large-file.zip').pipe(fs.createWriteStream('file.zip'));自动重试+超时控制
网络不稳定时,请求失败要自动重试。got 内置重试逻辑:
const got = require('got');
got.get('http://api.example.com/data', {
retry: { limit: 3 }, // 失败重试3次
timeout: { request: 5000 } // 5秒没响应算超时
}).then(res => console.log(res.body))
.catch(err => console.error('最终失败:', err));axios 得装插件(如 axios-retry):
const axios = require('axios');
const axiosRetry = require('axios-retry');
axiosRetry(axios, { retries: 3 }); // 全局生效
axios.get('http://api.example.com/data', { timeout: 5000 })
.then(res => console.log(res.data))
.catch(err => console.error('重试后失败:', err));异步处理和错误排查有啥技巧?
Node.js 里发请求几乎都是异步的,处理不好容易掉坑,这部分讲怎么理顺异步逻辑,快速定位错误。
用 Promise/async/await 管理异步
原生模块默认是回调风格,改成 Promise 更顺手,比如把 http.get 包成 Promise:
function getPromise(url) {
return new Promise((resolve, reject) => {
http.get(url, (res) => {
let data = '';
res.on('data', (chunk) => { data += chunk; });
res.on('end', () => resolve(data));
res.on('error', (err) => reject(err));
}).on('error', (err) => reject(err));
});
}
// 用async/await调用
(async () => {
try {
const data = await getPromise('http://api.example.com/data');
console.log(data);
} catch (err) {
console.error('请求失败:', err);
}
})();第三方库基本都支持 Promise,直接用 async/await 写同步风格代码,逻辑更清晰。
错误分类处理
请求可能遇到三类错误:
网络层错误:DNS 解析失败、连不上服务器(原生里是
req.on('error'),第三方库用catch捕获)。响应层错误:服务器返回 4xx(如参数错)、5xx(服务端崩了),原生要自己查
res.statusCode,第三方库(如axios)会把状态码非 2xx 的情况抛到catch。业务层错误:比如接口返回
{ code: 1001, msg: 'token过期' },得自己解析响应体判断。
举个 axios 处理所有错误的例子:
axios.get('http://api.example.com/data')
.then(res => {
if (res.data.code !== 0) { // 业务错误
return Promise.reject(new Error(res.data.msg));
}
return res.data;
})
.catch(err => {
if (err.response) { // 响应了但状态码不对(4xx/5xx)
console.error('服务端错误:', err.response.status, err.response.data);
} else if (err.request) { // 发了请求但没响应(超时、断网)
console.error('网络错误:', err.request);
} else { // 其他错误(如参数错)
console.error('请求前错误:', err.message);
}
});超时设置的坑
原生模块默认没超时,得自己加 setTimeout + abort:
const req = http.get(url, (res) => { /* 处理响应 */ });
const timeout = setTimeout(() => {
req.abort(); // 超时后终止请求
console.error('请求超时');
}, 5000);
req.on('end', () => clearTimeout(timeout)); // 成功后清定时器第三方库一般有 timeout 选项,直接配数字就行,axios 的 { timeout: 5000 }。
实战案例:从接口请求到数据处理
光说不练假把式,用几个真实场景练手。
案例1:调用天气API(原生 vs axios)
假设用“和风天气”API,先拿原生 http 实现:
const http = require('http');
const apiKey = '你的密钥';
const city = '北京';
const url = `https://devapi.qweather.com/v7/weather/now?location=${city}&key=${apiKey}`;
http.get(url, (res) => {
let data = '';
res.on('data', (chunk) => { data += chunk; });
res.on('end', () => {
const weather = JSON.parse(data);
console.log(`当前${city}天气:${weather.now.text},温度${weather.now.temp}°C`);
});
}).on('error', (err) => {
console.error('请求失败:', err);
});换成 axios 后,代码更简洁:
const axios = require('axios');
const apiKey = '你的密钥';
const city = '北京';
async function getWeather() {
try {
const res = await axios.get('https://devapi.qweather.com/v7/weather/now', {
params: { location: city, key: apiKey }
});
const { text, temp } = res.data.now;
console.log(`当前${city}天气:${text},温度${temp}°C`);
} catch (err) {
console.error('查天气失败:', err);
}
}
getWeather();案例2:分页接口循环请求
比如某接口返回列表,支持 page 参数,要把所有页数据捞回来,用 got 做并发控制:
const got = require('got');
const baseUrl = 'https://api.example.com/list';
const pageSize = 20;
let currentPage = 1;
const allData = [];
async function fetchPage() {
try {
const res = await got.get(baseUrl, {
searchParams: { page: currentPage, size: pageSize },
responseType: 'json'
});
allData.push(...res.body.items);
if (res.body.total > currentPage * pageSize) { // 还有下一页
currentPage++;
await fetchPage(); // 递归调自己
} else {
console.log('所有数据:', allData);
}
} catch (err) {
console.error('分页请求失败:', err);
}
}
fetchPage() 







网友评论文明上网理性发言 已有0人参与
发表评论: