Node.js HTTP 服务器开发

参考资源:


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 可以实现文件上传,但使用专门的库(如 formidablemulter)会更简单可靠:

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:利用多路复用、服务器推送等新特性
相关推荐
码途潇潇7 小时前
数据大屏常用布局-等比缩放布局(Scale Laylout)-使用 CSS Transform Scale 实现等比缩放
前端·css
犬大犬小7 小时前
从头说下DOM XSS
前端·javascript·xss
绿鸳7 小时前
Socket.IO实时通信
前端
Cache技术分享7 小时前
273. Java Stream API - Stream 中的中间操作:Mapping 操作详解
前端·后端
我的div丢了肿么办7 小时前
echarts中appendData的详细讲解
前端·javascript·vue.js
JamesGosling6667 小时前
async/defer 执行顺序全解析:从面试坑到 MDN 标准
前端·javascript
喝咖啡的女孩7 小时前
Web Worker 前端多线程解析
前端
一水鉴天7 小时前
整体设计 定稿 之6 完整设计文档讨论及定稿 之3 整体设计原则(原型-过程-模块三阶联动体系)
前端·数据库·人工智能