最小 HTTP 服务器
import http from 'http';
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello, World');
});
server.listen(8080, () => {
console.log('Server on http://localhost:8080');
});
req(IncomingMessage)
req.method // 'GET' / 'POST' / ...
req.url // '/api/users?id=1'
req.headers // 所有 header 对象
req.headers['user-agent']
req.httpVersion // '1.1'
req.socket.remoteAddress // 客户端 IP
res(ServerResponse)
res.writeHead(200, { 'Content-Type': 'application/json' });
res.write('part 1');
res.write('part 2');
res.end('done'); // 必须 end()
// 简写
res.setHeader('X-Custom', 'value');
res.statusCode = 404;
res.end('Not Found');
// 重定向
res.writeHead(302, { Location: '/new-path' });
res.end();
解析 URL + 查询参数
const url = new URL(req.url, `http://${req.headers.host}`);
url.pathname // '/api/users'
url.searchParams.get('id') // '1'
url.searchParams.getAll('tag') // ['a', 'b'](多个)
解析请求体
// 接收 JSON
server.on('request', async (req, res) => {
if (req.method === 'POST') {
let body = '';
for await (const chunk of req) {
body += chunk;
}
const data = JSON.parse(body);
// ...
}
});
或用现代 req.json()(在某些框架里)——原生 http 模块没这个。
路由(手动)
const server = http.createServer((req, res) => {
if (req.url === '/' && req.method === 'GET') {
return res.end('Home');
}
if (req.url === '/users' && req.method === 'GET') {
res.setHeader('Content-Type', 'application/json');
return res.end(JSON.stringify(users));
}
res.statusCode = 404;
res.end('Not Found');
});
生产应用用 Express / Fastify——见 27-28。
静态文件
import { createReadStream, statSync } from 'fs';
import { extname, join } from 'path';
const types = {
'.html': 'text/html',
'.css': 'text/css',
'.js': 'application/javascript',
'.json': 'application/json',
'.png': 'image/png',
};
http.createServer((req, res) => {
const file = join('./public', req.url === '/' ? '/index.html' : req.url);
try {
const ext = extname(file);
res.setHeader('Content-Type', types[ext] || 'application/octet-stream');
createReadStream(file).pipe(res);
} catch {
res.statusCode = 404;
res.end();
}
}).listen(8080);
HTTPS
import https from 'https';
import { readFileSync } from 'fs';
https.createServer({
key: readFileSync('key.pem'),
cert: readFileSync('cert.pem'),
}, (req, res) => {
res.end('Secure');
}).listen(443);
实战 90% 用 Nginx / Caddy 在前面终止 HTTPS,Node 后端只跑 HTTP。
优雅关闭
const server = http.createServer(...).listen(8080);
process.on('SIGTERM', () => {
console.log('SIGTERM received, closing...');
server.close(() => {
console.log('Server closed');
process.exit(0);
});
setTimeout(() => process.exit(1), 10000); // 强制 10 秒后退
});
错误处理
server.on('error', err => {
if (err.code === 'EADDRINUSE') {
console.error('Port already in use');
} else {
console.error(err);
}
});
server.on('clientError', (err, socket) => {
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
});
实战:常用 status code
200 OK
201 Created
204 No Content
301 Moved Permanently(永久重定向)
302 Found(临时重定向)
304 Not Modified(用了缓存)
400 Bad Request
401 Unauthorized(没登录)
403 Forbidden(已登录但无权限)
404 Not Found
429 Too Many Requests(限流)
500 Internal Server Error
502 Bad Gateway(上游挂)
503 Service Unavailable
504 Gateway Timeout
坑
- 必须
res.end()——不然请求挂起到超时 - header 必须在
write/end之前——writeHead之后改 header 没用 - 大响应别
res.end(bigString)——内存爆;用流pipe - 同步阻塞 = 全站慢——别在请求处理里跑 CPU 密集任务
下一篇:HTTP 客户端。