参考资源:
12.14 HTTP 服务器基础
12.14.1 创建服务器(createServer)
Node.js 提供了内置的 http 模块来创建 HTTP 服务器。使用 http.createServer() 方法可以创建一个 HTTP 服务器实例。
基本语法
javascript
const http = require('http');
const server = http.createServer((req, res) => {
// 请求处理逻辑
});
server.listen(port, hostname, callback);
创建简单的 HTTP 服务器
javascript
const http = require('http');
// 创建服务器
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello World!');
});
// 监听端口
server.listen(8080, () => {
console.log('服务器运行在 http://localhost:8080');
});
使用 ES6 模块语法
javascript
import http from 'http';
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end('<h1>Hello World!</h1>');
});
server.listen(8080);
服务器配置选项
javascript
const http = require('http');
const server = http.createServer({
// 请求超时时间(毫秒)
requestTimeout: 30000,
// 请求头超时时间(毫秒)
headersTimeout: 60000,
// 保持连接的超时时间(毫秒)
keepAliveTimeout: 5000,
// 最大请求头大小(字节)
maxHeadersCount: 2000
}, (req, res) => {
res.end('Server configured');
});
server.listen(8080);
12.14.2 请求处理(req)
req 对象是 http.IncomingMessage 的实例,包含了客户端请求的所有信息。
请求对象的主要属性
javascript
const http = require('http');
const server = http.createServer((req, res) => {
// 请求方法(GET, POST, PUT, DELETE 等)
console.log('Method:', req.method);
// 请求 URL
console.log('URL:', req.url);
// HTTP 版本
console.log('HTTP Version:', req.httpVersion);
// 请求头对象
console.log('Headers:', req.headers);
// 获取特定请求头
console.log('User-Agent:', req.headers['user-agent']);
console.log('Content-Type:', req.headers['content-type']);
// 请求的原始 URL(包含查询字符串)
console.log('Raw URL:', req.url);
res.end('Request received');
});
server.listen(8080);
解析 URL 和查询参数
javascript
const http = require('http');
const url = require('url');
const server = http.createServer((req, res) => {
// 解析 URL
const parsedUrl = url.parse(req.url, true);
console.log('Pathname:', parsedUrl.pathname);
console.log('Query:', parsedUrl.query);
// 例如:/api/users?id=123&name=John
// parsedUrl.query = { id: '123', name: 'John' }
res.end('URL parsed');
});
server.listen(8080);
读取请求体(POST/PUT 数据)
javascript
const http = require('http');
const server = http.createServer((req, res) => {
let body = '';
// 监听数据流
req.on('data', (chunk) => {
body += chunk.toString();
});
// 数据接收完成
req.on('end', () => {
try {
// 解析 JSON 数据
const data = JSON.parse(body);
console.log('Received data:', data);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: true, received: data }));
} catch (error) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Invalid JSON' }));
}
});
// 错误处理
req.on('error', (error) => {
console.error('Request error:', error);
res.writeHead(500);
res.end('Server error');
});
});
server.listen(8080);
使用流式处理大文件
javascript
const http = require('http');
const fs = require('fs');
const server = http.createServer((req, res) => {
// 直接将请求流写入文件
const writeStream = fs.createWriteStream('uploaded-file.txt');
req.pipe(writeStream);
req.on('end', () => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('File uploaded successfully');
});
});
server.listen(8080);
12.14.3 响应处理(res)
res 对象是 http.ServerResponse 的实例,用于向客户端发送响应。
设置响应头
javascript
const http = require('http');
const server = http.createServer((req, res) => {
// 方法1:使用 writeHead(必须在 write 或 end 之前调用)
res.writeHead(200, {
'Content-Type': 'text/html',
'Cache-Control': 'no-cache',
'X-Custom-Header': 'Custom Value'
});
// 方法2:使用 setHeader(可以多次调用)
res.setHeader('Content-Type', 'application/json');
res.setHeader('X-Powered-By', 'Node.js');
// 获取已设置的响应头
console.log('Content-Type:', res.getHeader('Content-Type'));
// 移除响应头
res.removeHeader('X-Powered-By');
res.end('Response sent');
});
server.listen(8080);
发送响应数据
javascript
const http = require('http');
const server = http.createServer((req, res) => {
// 方法1:使用 res.end() 一次性发送
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello World');
// 方法2:分块发送数据
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write('<html>');
res.write('<head><title>My Page</title></head>');
res.write('<body>');
res.write('<h1>Hello World</h1>');
res.write('</body>');
res.write('</html>');
res.end(); // 必须调用 end() 来结束响应
});
server.listen(8080);
HTTP 状态码
javascript
const http = require('http');
const server = http.createServer((req, res) => {
const statusCode = 200; // 成功
// 常见状态码
// 200 - OK(成功)
// 201 - Created(已创建)
// 204 - No Content(无内容)
// 301 - Moved Permanently(永久重定向)
// 302 - Found(临时重定向)
// 400 - Bad Request(错误请求)
// 401 - Unauthorized(未授权)
// 403 - Forbidden(禁止访问)
// 404 - Not Found(未找到)
// 500 - Internal Server Error(服务器错误)
// 502 - Bad Gateway(网关错误)
// 503 - Service Unavailable(服务不可用)
res.writeHead(statusCode, { 'Content-Type': 'text/plain' });
res.end(`Status Code: ${statusCode}`);
});
server.listen(8080);
发送 JSON 响应
javascript
const http = require('http');
const server = http.createServer((req, res) => {
const data = {
id: 1,
name: 'John Doe',
email: 'john@example.com'
};
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(data));
});
server.listen(8080);
重定向
javascript
const http = require('http');
const server = http.createServer((req, res) => {
if (req.url === '/old') {
// 301 永久重定向
res.writeHead(301, {
'Location': '/new'
});
res.end();
} else if (req.url === '/new') {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end('<h1>New Page</h1>');
} else {
res.writeHead(404);
res.end('Not Found');
}
});
server.listen(8080);
错误处理
javascript
const http = require('http');
const server = http.createServer((req, res) => {
try {
// 模拟错误
if (req.url === '/error') {
throw new Error('Something went wrong');
}
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Success');
} catch (error) {
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
error: error.message
}));
}
});
server.listen(8080);
12.14.4 路由处理
路由处理是根据不同的 URL 路径和方法执行不同的处理逻辑。
基础路由实现
javascript
const http = require('http');
const url = require('url');
const server = http.createServer((req, res) => {
const parsedUrl = url.parse(req.url, true);
const path = parsedUrl.pathname;
const method = req.method;
// 路由表
if (path === '/' && method === 'GET') {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end('<h1>Home Page</h1>');
} else if (path === '/about' && method === 'GET') {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end('<h1>About Page</h1>');
} else if (path === '/api/users' && method === 'GET') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify([
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' }
]));
} else if (path === '/api/users' && method === 'POST') {
// 处理 POST 请求
let body = '';
req.on('data', chunk => body += chunk);
req.on('end', () => {
const user = JSON.parse(body);
res.writeHead(201, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ id: Date.now(), ...user }));
});
} else {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('404 Not Found');
}
});
server.listen(8080);
使用路由对象管理路由
javascript
const http = require('http');
const url = require('url');
// 路由处理器对象
const routes = {
'GET /': (req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end('<h1>Home</h1>');
},
'GET /about': (req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end('<h1>About</h1>');
},
'GET /api/users': (req, res) => {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify([{ id: 1, name: 'John' }]));
},
'POST /api/users': (req, res) => {
let body = '';
req.on('data', chunk => body += chunk);
req.on('end', () => {
res.writeHead(201, { 'Content-Type': 'application/json' });
res.end(body);
});
}
};
const server = http.createServer((req, res) => {
const parsedUrl = url.parse(req.url, true);
const routeKey = `${req.method} ${parsedUrl.pathname}`;
const handler = routes[routeKey];
if (handler) {
handler(req, res);
} else {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('404 Not Found');
}
});
server.listen(8080);
支持动态路由参数
javascript
const http = require('http');
const url = require('url');
// 简单的路由匹配函数
function matchRoute(pattern, path) {
const patternParts = pattern.split('/');
const pathParts = path.split('/');
if (patternParts.length !== pathParts.length) {
return null;
}
const params = {};
for (let i = 0; i < patternParts.length; i++) {
if (patternParts[i].startsWith(':')) {
const paramName = patternParts[i].slice(1);
params[paramName] = pathParts[i];
} else if (patternParts[i] !== pathParts[i]) {
return null;
}
}
return params;
}
const server = http.createServer((req, res) => {
const parsedUrl = url.parse(req.url, true);
const path = parsedUrl.pathname;
// 匹配 /api/users/:id
const userParams = matchRoute('/api/users/:id', path);
if (userParams && req.method === 'GET') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
id: userParams.id,
name: `User ${userParams.id}`
}));
return;
}
// 匹配 /api/posts/:postId/comments/:commentId
const commentParams = matchRoute('/api/posts/:postId/comments/:commentId', path);
if (commentParams && req.method === 'GET') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
postId: commentParams.postId,
commentId: commentParams.commentId
}));
return;
}
res.writeHead(404);
res.end('Not Found');
});
server.listen(8080);
RESTful API 路由示例
javascript
const http = require('http');
const url = require('url');
const users = [
{ id: 1, name: 'John', email: 'john@example.com' },
{ id: 2, name: 'Jane', email: 'jane@example.com' }
];
const server = http.createServer((req, res) => {
const parsedUrl = url.parse(req.url, true);
const path = parsedUrl.pathname;
const method = req.method;
// GET /api/users - 获取所有用户
if (path === '/api/users' && method === 'GET') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(users));
}
// GET /api/users/:id - 获取单个用户
else if (path.startsWith('/api/users/') && method === 'GET') {
const id = parseInt(path.split('/')[3]);
const user = users.find(u => u.id === id);
if (user) {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(user));
} else {
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'User not found' }));
}
}
// POST /api/users - 创建用户
else if (path === '/api/users' && method === 'POST') {
let body = '';
req.on('data', chunk => body += chunk);
req.on('end', () => {
try {
const newUser = JSON.parse(body);
newUser.id = users.length + 1;
users.push(newUser);
res.writeHead(201, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(newUser));
} catch (error) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Invalid JSON' }));
}
});
}
// PUT /api/users/:id - 更新用户
else if (path.startsWith('/api/users/') && method === 'PUT') {
const id = parseInt(path.split('/')[3]);
const index = users.findIndex(u => u.id === id);
if (index !== -1) {
let body = '';
req.on('data', chunk => body += chunk);
req.on('end', () => {
try {
const updatedUser = JSON.parse(body);
users[index] = { ...users[index], ...updatedUser };
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(users[index]));
} catch (error) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Invalid JSON' }));
}
});
} else {
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'User not found' }));
}
}
// DELETE /api/users/:id - 删除用户
else if (path.startsWith('/api/users/') && method === 'DELETE') {
const id = parseInt(path.split('/')[3]);
const index = users.findIndex(u => u.id === id);
if (index !== -1) {
users.splice(index, 1);
res.writeHead(204);
res.end();
} else {
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'User not found' }));
}
}
else {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('Not Found');
}
});
server.listen(8080);
12.15 HTTP 服务器高级
12.15.1 中间件模式
中间件模式是 Node.js Web 开发中的重要概念,允许在请求和响应之间执行多个处理函数。
基础中间件实现
javascript
const http = require('http');
const url = require('url');
// 中间件数组
const middlewares = [];
// 添加中间件的函数
function use(middleware) {
middlewares.push(middleware);
}
// 执行中间件链
function executeMiddlewares(req, res, index = 0) {
if (index >= middlewares.length) {
return;
}
const middleware = middlewares[index];
middleware(req, res, () => {
executeMiddlewares(req, res, index + 1);
});
}
// 日志中间件
use((req, res, next) => {
console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`);
next();
});
// 解析 JSON 中间件
use((req, res, next) => {
if (req.headers['content-type'] === 'application/json') {
let body = '';
req.on('data', chunk => body += chunk);
req.on('end', () => {
try {
req.body = JSON.parse(body);
} catch (error) {
req.body = {};
}
next();
});
} else {
next();
}
});
// CORS 中间件
use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
next();
});
const server = http.createServer((req, res) => {
executeMiddlewares(req, res, () => {
// 路由处理
if (req.url === '/') {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello World');
} else {
res.writeHead(404);
res.end('Not Found');
}
});
});
server.listen(8080);
基于类的中间件系统
javascript
const http = require('http');
class Middleware {
constructor() {
this.middlewares = [];
}
use(fn) {
this.middlewares.push(fn);
return this;
}
handle(req, res) {
let index = 0;
const next = () => {
if (index < this.middlewares.length) {
const middleware = this.middlewares[index++];
middleware(req, res, next);
}
};
next();
}
}
const app = new Middleware();
// 请求日志中间件
app.use((req, res, next) => {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
next();
});
// 请求体解析中间件
app.use((req, res, next) => {
if (req.method === 'POST' || req.method === 'PUT') {
let body = '';
req.on('data', chunk => body += chunk);
req.on('end', () => {
req.body = body;
next();
});
} else {
next();
}
});
// 错误处理中间件
app.use((req, res, next) => {
res.on('error', (error) => {
console.error('Response error:', error);
});
next();
});
const server = http.createServer((req, res) => {
app.handle(req, res);
// 路由处理
if (req.url === '/') {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello World');
} else {
res.writeHead(404);
res.end('Not Found');
}
});
server.listen(8080);
常见中间件示例
javascript
const http = require('http');
// 1. 请求日志中间件
function logger(req, res, next) {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
console.log(`${req.method} ${req.url} ${res.statusCode} ${duration}ms`);
});
next();
}
// 2. 身份验证中间件
function authenticate(req, res, next) {
const token = req.headers['authorization'];
if (token === 'Bearer secret-token') {
req.user = { id: 1, name: 'John' };
next();
} else {
res.writeHead(401, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Unauthorized' }));
}
}
// 3. 请求体大小限制中间件
function bodySizeLimit(maxSize) {
return (req, res, next) => {
let size = 0;
req.on('data', (chunk) => {
size += chunk.length;
if (size > maxSize) {
res.writeHead(413, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Request entity too large' }));
return;
}
});
req.on('end', () => {
next();
});
};
}
// 4. 速率限制中间件
function rateLimiter(maxRequests, windowMs) {
const requests = new Map();
return (req, res, next) => {
const ip = req.headers['x-forwarded-for'] || '127.0.0.1';
const now = Date.now();
const windowStart = now - windowMs;
if (!requests.has(ip)) {
requests.set(ip, []);
}
const ipRequests = requests.get(ip);
const recentRequests = ipRequests.filter(time => time > windowStart);
if (recentRequests.length >= maxRequests) {
res.writeHead(429, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Too many requests' }));
return;
}
recentRequests.push(now);
requests.set(ip, recentRequests);
next();
};
}
12.15.2 静态文件服务
静态文件服务是指提供 HTML、CSS、JavaScript、图片等静态资源文件。
基础静态文件服务器
javascript
const http = require('http');
const fs = require('fs');
const path = require('path');
const server = http.createServer((req, res) => {
// 获取文件路径
let filePath = '.' + req.url;
if (filePath === './') {
filePath = './index.html';
}
// 获取文件扩展名
const extname = String(path.extname(filePath)).toLowerCase();
// MIME 类型映射
const mimeTypes = {
'.html': 'text/html',
'.js': 'text/javascript',
'.css': 'text/css',
'.json': 'application/json',
'.png': 'image/png',
'.jpg': 'image/jpg',
'.gif': 'image/gif',
'.svg': 'image/svg+xml',
'.wav': 'audio/wav',
'.mp4': 'video/mp4',
'.woff': 'application/font-woff',
'.ttf': 'application/font-ttf',
'.eot': 'application/vnd.ms-fontobject',
'.otf': 'application/font-otf',
'.wasm': 'application/wasm'
};
const contentType = mimeTypes[extname] || 'application/octet-stream';
// 读取文件
fs.readFile(filePath, (error, content) => {
if (error) {
if (error.code === 'ENOENT') {
res.writeHead(404, { 'Content-Type': 'text/html' });
res.end('<h1>404 - File Not Found</h1>');
} else {
res.writeHead(500);
res.end(`Server Error: ${error.code}`);
}
} else {
res.writeHead(200, { 'Content-Type': contentType });
res.end(content, 'utf-8');
}
});
});
server.listen(8080);
使用流式传输优化性能
javascript
const http = require('http');
const fs = require('fs');
const path = require('path');
const server = http.createServer((req, res) => {
let filePath = './public' + req.url;
if (filePath === './public/') {
filePath = './public/index.html';
}
const extname = String(path.extname(filePath)).toLowerCase();
const mimeTypes = {
'.html': 'text/html',
'.js': 'text/javascript',
'.css': 'text/css',
'.json': 'application/json',
'.png': 'image/png',
'.jpg': 'image/jpg',
'.gif': 'image/gif',
'.svg': 'image/svg+xml'
};
const contentType = mimeTypes[extname] || 'application/octet-stream';
// 检查文件是否存在
fs.access(filePath, fs.constants.F_OK, (err) => {
if (err) {
res.writeHead(404, { 'Content-Type': 'text/html' });
res.end('<h1>404 - File Not Found</h1>');
return;
}
// 获取文件信息
fs.stat(filePath, (err, stats) => {
if (err) {
res.writeHead(500);
res.end('Server Error');
return;
}
// 设置响应头
res.writeHead(200, {
'Content-Type': contentType,
'Content-Length': stats.size,
'Cache-Control': 'public, max-age=3600'
});
// 使用流式传输
const fileStream = fs.createReadStream(filePath);
fileStream.pipe(res);
fileStream.on('error', (error) => {
res.writeHead(500);
res.end('File read error');
});
});
});
});
server.listen(8080);
支持范围请求(Range Requests)
javascript
const http = require('http');
const fs = require('fs');
const path = require('path');
const server = http.createServer((req, res) => {
const filePath = './public' + req.url;
fs.stat(filePath, (err, stats) => {
if (err) {
res.writeHead(404);
res.end('Not Found');
return;
}
const range = req.headers.range;
if (range) {
// 解析范围请求
const parts = range.replace(/bytes=/, '').split('-');
const start = parseInt(parts[0], 10);
const end = parts[1] ? parseInt(parts[1], 10) : stats.size - 1;
const chunksize = (end - start) + 1;
// 设置部分内容响应头
res.writeHead(206, {
'Content-Range': `bytes ${start}-${end}/${stats.size}`,
'Accept-Ranges': 'bytes',
'Content-Length': chunksize,
'Content-Type': 'video/mp4'
});
// 创建读取流
const fileStream = fs.createReadStream(filePath, { start, end });
fileStream.pipe(res);
} else {
// 完整文件响应
res.writeHead(200, {
'Content-Length': stats.size,
'Content-Type': 'video/mp4'
});
fs.createReadStream(filePath).pipe(res);
}
});
});
server.listen(8080);
12.15.3 文件上传
文件上传是 Web 应用中的常见需求,Node.js 可以处理 multipart/form-data 格式的文件上传。
基础文件上传处理
javascript
const http = require('http');
const fs = require('fs');
const path = require('path');
const server = http.createServer((req, res) => {
if (req.method === 'POST' && req.url === '/upload') {
const uploadDir = './uploads';
// 确保上传目录存在
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir);
}
let body = '';
req.on('data', (chunk) => {
body += chunk.toString('binary');
});
req.on('end', () => {
/* body 大致格式为:
* --boundary值\r\n
* Content-Disposition: form-data; name="字段名"; filename="文件名"\r\n
* Content-Type: 文件类型\r\n
* \r\n
* 文件二进制内容
* \r\n
* --boundary值\r\n
* Content-Disposition: form-data; name="其他字段名"\r\n
* \r\n
* 字段值
* \r\n
* --boundary值--\r\n
*
* 示例:
* ----------------------------268888137557675578696767\r\n
* Content-Disposition: form-data; name="aaa"; filename="test.png"\r\n
* Content-Type: image/png\r\n
* \r\n
* [PNG文件二进制数据]\r\n
* ----------------------------268888137557675578696767--\r\n
*/
// 解析 multipart/form-data
const boundary = req.headers['content-type'].split('boundary=')[1];
// 使用 boundary 分割各个部分(注意:boundary 前需要加 --)
const parts = body.split('--' + boundary);
for (const part of parts) {
if (part.includes('Content-Disposition')) {
const filenameMatch = part.match(/filename="(.+)"/);
if (filenameMatch) {
const filename = filenameMatch[1];
const fileContent = part.split('\r\n\r\n')[1].split('\r\n--')[0];
const filePath = path.join(uploadDir, filename);
fs.writeFileSync(filePath, fileContent, 'binary');
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
success: true,
filename: filename,
message: 'File uploaded successfully'
}));
return;
}
}
}
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'No file uploaded' }));
});
} else {
res.writeHead(404);
res.end('Not Found');
}
});
server.listen(8080);
使用流式处理大文件上传
javascript
const http = require('http');
const fs = require('fs');
const path = require('path');
const { Transform } = require('stream');
class MultipartParser extends Transform {
constructor(boundary) {
super();
this.boundary = Buffer.from(`--${boundary}`);
this.buffer = Buffer.alloc(0);
this.currentFile = null;
}
_transform(chunk, encoding, callback) {
this.buffer = Buffer.concat([this.buffer, chunk]);
this.processBuffer();
callback();
}
processBuffer() {
let boundaryIndex = this.buffer.indexOf(this.boundary);
while (boundaryIndex !== -1) {
if (this.currentFile) {
// 保存当前文件
this.currentFile.end();
this.currentFile = null;
}
// 查找下一个边界
const nextBoundaryIndex = this.buffer.indexOf(
this.boundary,
boundaryIndex + this.boundary.length
);
if (nextBoundaryIndex === -1) {
break;
}
// 提取文件数据
const part = this.buffer.slice(
boundaryIndex + this.boundary.length,
nextBoundaryIndex
);
this.processPart(part);
this.buffer = this.buffer.slice(nextBoundaryIndex);
boundaryIndex = this.buffer.indexOf(this.boundary);
}
}
processPart(part) {
const headerEnd = part.indexOf('\r\n\r\n');
if (headerEnd === -1) return;
const headers = part.slice(0, headerEnd).toString();
const contentDisposition = headers.match(/Content-Disposition:.*filename="(.+)"/);
if (contentDisposition) {
const filename = contentDisposition[1];
const filePath = path.join('./uploads', filename);
this.currentFile = fs.createWriteStream(filePath);
const fileData = part.slice(headerEnd + 4);
this.currentFile.write(fileData);
}
}
_flush(callback) {
if (this.currentFile) {
this.currentFile.end();
}
callback();
}
}
const server = http.createServer((req, res) => {
if (req.method === 'POST' && req.url === '/upload') {
const contentType = req.headers['content-type'];
const boundary = contentType.split('boundary=')[1];
const parser = new MultipartParser(boundary);
parser.on('finish', () => {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: true, message: 'Files uploaded' }));
});
req.pipe(parser);
} else {
res.writeHead(404);
res.end('Not Found');
}
});
server.listen(8080);
使用第三方库处理文件上传(推荐)
虽然使用原生 Node.js 可以实现文件上传,但使用专门的库(如 formidable 或 multer)会更简单可靠:
javascript
// 使用 formidable 的示例(需要先安装:npm install formidable)
const http = require('http');
const formidable = require('formidable');
const fs = require('fs');
const server = http.createServer((req, res) => {
if (req.method === 'POST' && req.url === '/upload') {
const form = formidable({
uploadDir: './uploads',
keepExtensions: true,
maxFileSize: 10 * 1024 * 1024 // 10MB
});
form.parse(req, (err, fields, files) => {
if (err) {
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: err.message }));
return;
}
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
success: true,
files: Object.keys(files).map(key => ({
name: files[key].originalFilename,
path: files[key].filepath,
size: files[key].size
}))
}));
});
} else {
res.writeHead(404);
res.end('Not Found');
}
});
server.listen(8080);
12.15.4 HTTPS
HTTPS 是 HTTP 的安全版本,使用 SSL/TLS 加密传输数据。
创建 HTTPS 服务器
javascript
const https = require('https');
const fs = require('fs');
// 读取 SSL 证书和私钥
const options = {
key: fs.readFileSync('private-key.pem'),
cert: fs.readFileSync('certificate.pem')
};
const server = https.createServer(options, (req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello HTTPS World!');
});
server.listen(443, () => {
console.log('HTTPS Server running on https://localhost:443');
});
生成自签名证书(用于开发)
bash
# 生成私钥
openssl genrsa -out private-key.pem 2048
# 生成证书签名请求
openssl req -new -key private-key.pem -out csr.pem
# 生成自签名证书(有效期365天)
openssl x509 -req -days 365 -in csr.pem -signkey private-key.pem -out certificate.pem
HTTP 和 HTTPS 服务器共存
javascript
const http = require('http');
const https = require('https');
const fs = require('fs');
// HTTP 服务器(重定向到 HTTPS)
const httpServer = http.createServer((req, res) => {
res.writeHead(301, {
'Location': `https://${req.headers.host}${req.url}`
});
res.end();
});
// HTTPS 服务器
const httpsOptions = {
key: fs.readFileSync('private-key.pem'),
cert: fs.readFileSync('certificate.pem')
};
const httpsServer = https.createServer(httpsOptions, (req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end('<h1>Secure Connection</h1>');
});
httpServer.listen(80, () => {
console.log('HTTP Server (redirect) running on port 80');
});
httpsServer.listen(443, () => {
console.log('HTTPS Server running on port 443');
});
使用 Let's Encrypt 免费证书
javascript
const https = require('https');
const fs = require('fs');
// Let's Encrypt 证书路径(通常由 certbot 管理)
const options = {
key: fs.readFileSync('/etc/letsencrypt/live/example.com/privkey.pem'),
cert: fs.readFileSync('/etc/letsencrypt/live/example.com/fullchain.pem')
};
const server = https.createServer(options, (req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello Secure World!');
});
server.listen(443);
12.15.5 HTTP/2
HTTP/2 是 HTTP 协议的第二个主要版本,提供了多路复用、服务器推送等新特性。
创建 HTTP/2 服务器
javascript
const http2 = require('http2');
const fs = require('fs');
const options = {
key: fs.readFileSync('private-key.pem'),
cert: fs.readFileSync('certificate.pem')
};
const server = http2.createSecureServer(options, (req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end('<h1>Hello HTTP/2!</h1>');
});
server.listen(8443, () => {
console.log('HTTP/2 Server running on https://localhost:8443');
});
HTTP/2 服务器推送
javascript
const http2 = require('http2');
const fs = require('fs');
const options = {
key: fs.readFileSync('private-key.pem'),
cert: fs.readFileSync('certificate.pem')
};
const server = http2.createSecureServer(options, (req, res) => {
if (req.url === '/') {
// 推送 CSS 文件
res.stream.pushStream({ ':path': '/style.css' }, (err, pushStream) => {
if (!err) {
pushStream.respond({ ':status': 200, 'content-type': 'text/css' });
pushStream.end('body { background: #f0f0f0; }');
}
});
// 推送 JavaScript 文件
res.stream.pushStream({ ':path': '/script.js' }, (err, pushStream) => {
if (!err) {
pushStream.respond({ ':status': 200, 'content-type': 'application/javascript' });
pushStream.end('console.log("Hello from pushed script");');
}
});
// 主响应
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(`
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/style.css">
</head>
<body>
<h1>HTTP/2 Server Push Example</h1>
<script src="/script.js"></script>
</body>
</html>
`);
} else {
res.writeHead(404);
res.end('Not Found');
}
});
server.listen(8443);
HTTP/2 多路复用示例
javascript
const http2 = require('http2');
const fs = require('fs');
const options = {
key: fs.readFileSync('private-key.pem'),
cert: fs.readFileSync('certificate.pem')
};
const server = http2.createSecureServer(options);
server.on('stream', (stream, headers) => {
const path = headers[':path'];
// 模拟不同的响应时间
if (path === '/fast') {
setTimeout(() => {
stream.respond({ ':status': 200, 'content-type': 'text/plain' });
stream.end('Fast response');
}, 100);
} else if (path === '/slow') {
setTimeout(() => {
stream.respond({ ':status': 200, 'content-type': 'text/plain' });
stream.end('Slow response');
}, 2000);
} else {
stream.respond({ ':status': 404 });
stream.end('Not Found');
}
});
server.listen(8443, () => {
console.log('HTTP/2 Server running on https://localhost:8443');
});
HTTP/2 与 HTTP/1.1 兼容性处理
javascript
const http = require('http');
const http2 = require('http2');
const fs = require('fs');
const httpsOptions = {
key: fs.readFileSync('private-key.pem'),
cert: fs.readFileSync('certificate.pem')
};
// HTTP/2 服务器
const http2Server = http2.createSecureServer(httpsOptions, (req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('HTTP/2 Response');
});
// HTTP/1.1 服务器(降级支持)
const httpServer = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('HTTP/1.1 Response');
});
http2Server.listen(8443, () => {
console.log('HTTP/2 Server on port 8443');
});
httpServer.listen(8080, () => {
console.log('HTTP/1.1 Server on port 8080');
});
总结
本文介绍了 Node.js HTTP 服务器开发的内容:
基础部分
- 创建服务器 :使用
http.createServer()创建 HTTP 服务器 - 请求处理 :通过
req对象获取请求信息、解析 URL、读取请求体 - 响应处理 :使用
res对象设置响应头、发送数据、处理状态码 - 路由处理:实现基础路由、动态路由、RESTful API
高级部分
- 中间件模式:实现请求处理链、日志、认证、错误处理等中间件
- 静态文件服务:提供静态资源文件、支持流式传输、范围请求
- 文件上传:处理 multipart/form-data、流式上传大文件
- HTTPS:创建安全连接、使用 SSL/TLS 证书
- HTTP/2:利用多路复用、服务器推送等新特性