Node.js Web 模块
http/https 客户端、fetch、WebSocket、以及服务端与客户端两侧。
Key PointsNode 18+ 用内置 `fetch` 发请求;需要细粒度控制时用 `http.request`。
服务端与客户端
| Role | API | Typical use |
|---|---|---|
| 服务端 | http.createServer | 接收请求 |
| 客户端 | fetch | 调第三方 API |
| 客户端 | http.request | 需要控制 header/超时/流 |
| 双向 | WebSocket(需 ws 包) | 实时推送 |
fetch
JavaScript
// GET
const res = await fetch('https://api.github.com/repos/nodejs/node');
console.log(res.status);
const repo = await res.json();
console.log(repo.stargazers_count);
// POST JSON
const created = await fetch('https://httpbin.org/post', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'tom' }),
signal: AbortSignal.timeout(5000),
});
console.log(await created.json());Output
200
107324
{ ... "json": { "name": "tom" } ... }fetch rejects only on network failures. HTTP 404 and 500 count as success, so check res.ok yourself. This is the easiest thing to forget when migrating from axios.
http.request
JavaScript
const https = require('node:https');
function get(url) {
return new Promise((resolve, reject) => {
const req = https.get(url, { timeout: 5000 }, (res) => {
if (res.statusCode !== 200) {
res.resume(); // 丢弃响应体,释放连接
return reject(new Error(`HTTP ${res.statusCode}`));
}
let data = '';
res.setEncoding('utf8');
res.on('data', (c) => (data += c));
res.on('end', () => resolve(JSON.parse(data)));
});
req.on('timeout', () => req.destroy(new Error('超时')));
req.on('error', reject);
});
}When the status is not 200, **always call
res.resume()** — otherwise the body is never drained, the socket is not released, and the connection pool fills up. The most hidden http.request trap.并发请求
JavaScript
const urls = ['https://a.com/1', 'https://a.com/2', 'https://a.com/3'];
// 全部并发
const all = await Promise.all(urls.map((u) => fetch(u).then((r) => r.text())));
// 限制并发为 2
async function pool(tasks, limit) {
const results = [];
const running = new Set();
for (const task of tasks) {
const p = task().finally(() => running.delete(p));
running.add(p);
results.push(p);
if (running.size >= limit) await Promise.race(running);
}
return Promise.all(results);
}WebSocket
JavaScript
// npm i ws
const { WebSocketServer } = require('ws');
const wss = new WebSocketServer({ port: 8080 });
wss.on('connection', (ws) => {
ws.send('欢迎');
ws.on('message', (msg) => {
// 广播给所有人
for (const c of wss.clients) {
if (c.readyState === c.OPEN) c.send(msg.toString());
}
});
});
console.log('ws://127.0.0.1:8080');Output
ws://127.0.0.1:8080服务端与客户端
| 角色 | API | 典型用途 |
|---|---|---|
| 服务端 | http.createServer | 接收请求 |
| 客户端 | fetch | 调第三方 API |
| 客户端 | http.request | 需要控制 header/超时/流 |
| 双向 | WebSocket(需 ws 包) | 实时推送 |
fetch
JavaScript
// GET
const res = await fetch('https://api.github.com/repos/nodejs/node');
console.log(res.status);
const repo = await res.json();
console.log(repo.stargazers_count);
// POST JSON
const created = await fetch('https://httpbin.org/post', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'tom' }),
signal: AbortSignal.timeout(5000),
});
console.log(await created.json());Output
200
107324
{ ... "json": { "name": "tom" } ... }fetch 只在网络层失败时 reject。HTTP 404、500 都算成功,必须自己检查 res.ok。这是从 axios 迁过来最容易忘的一点。
http.request
JavaScript
const https = require('node:https');
function get(url) {
return new Promise((resolve, reject) => {
const req = https.get(url, { timeout: 5000 }, (res) => {
if (res.statusCode !== 200) {
res.resume(); // 丢弃响应体,释放连接
return reject(new Error(`HTTP ${res.statusCode}`));
}
let data = '';
res.setEncoding('utf8');
res.on('data', (c) => (data += c));
res.on('end', () => resolve(JSON.parse(data)));
});
req.on('timeout', () => req.destroy(new Error('超时')));
req.on('error', reject);
});
}状态码不是 200 时**一定要
res.resume()**,否则响应体没读完,socket 不释放,连接池很快被占满。这是 http.request 最隐蔽的坑。并发请求
JavaScript
const urls = ['https://a.com/1', 'https://a.com/2', 'https://a.com/3'];
// 全部并发
const all = await Promise.all(urls.map((u) => fetch(u).then((r) => r.text())));
// 限制并发为 2
async function pool(tasks, limit) {
const results = [];
const running = new Set();
for (const task of tasks) {
const p = task().finally(() => running.delete(p));
running.add(p);
results.push(p);
if (running.size >= limit) await Promise.race(running);
}
return Promise.all(results);
}WebSocket
JavaScript
// npm i ws
const { WebSocketServer } = require('ws');
const wss = new WebSocketServer({ port: 8080 });
wss.on('connection', (ws) => {
ws.send('欢迎');
ws.on('message', (msg) => {
// 广播给所有人
for (const c of wss.clients) {
if (c.readyState === c.OPEN) c.send(msg.toString());
}
});
});
console.log('ws://127.0.0.1:8080');Output
ws://127.0.0.1:8080