Skip to content

Node.js HTTP 模块面试题

1. HTTP 模块基础

问题:Node.js 的 HTTP 模块是什么?如何创建一个基本的 HTTP 服务器?

答案: HTTP 模块是 Node.js 内置的核心模块,用于创建 HTTP 服务器和客户端。

基本 HTTP 服务器

javascript
const http = require('http');

// 创建服务器
const server = http.createServer((req, res) => {
  // 设置响应头
  res.writeHead(200, {
    'Content-Type': 'text/plain; charset=utf-8'
  });
  
  // 发送响应
  res.end('Hello World\n');
});

// 监听端口
server.listen(3000, () => {
  console.log('Server running at http://localhost:3000/');
});

处理不同路由

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;
  
  // 路由处理
  switch (path) {
    case '/':
      res.writeHead(200, { 'Content-Type': 'text/plain' });
      res.end('Home Page');
      break;
      
    case '/users':
      if (req.method === 'GET') {
        res.writeHead(200, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify({ users: [] }));
      } else if (req.method === 'POST') {
        // 处理 POST 请求
        handlePostRequest(req, res);
      }
      break;
      
    case '/about':
      res.writeHead(200, { 'Content-Type': 'text/html' });
      res.end('<h1>About Page</h1>');
      break;
      
    default:
      res.writeHead(404, { 'Content-Type': 'text/plain' });
      res.end('Not Found');
  }
});

function handlePostRequest(req, res) {
  let body = '';
  req.on('data', chunk => {
    body += chunk.toString();
  });
  req.on('end', () => {
    res.writeHead(201, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ received: body }));
  });
}

server.listen(3000);

2. 请求和响应对象

问题:HTTP 模块中的请求(req)和响应(res)对象有哪些常用属性和方法?

答案

请求对象(http.IncomingMessage)

javascript
http.createServer((req, res) => {
  // URL 相关
  console.log(req.url);        // 完整 URL 路径
  console.log(req.method);     // HTTP 方法 (GET, POST, etc.)
  console.log(req.headers);    // 请求头对象
  
  // 解析 URL
  const parsedUrl = url.parse(req.url, true);
  console.log(parsedUrl.pathname);  // 路径
  console.log(parsedUrl.query);     // 查询参数
  
  // 读取请求体
  let body = '';
  req.on('data', chunk => {
    body += chunk;
  });
  req.on('end', () => {
    console.log('Request body:', body);
  });
  
  // 其他属性
  console.log(req.httpVersion);   // HTTP 版本
  console.log(req.statusCode);    // 状态码(客户端请求时为 undefined)
  console.log(req.statusMessage); // 状态消息
  console.log(req.socket);        // 底层 socket
});

响应对象(http.ServerResponse)

javascript
http.createServer((req, res) => {
  // 设置状态码
  res.statusCode = 200;
  
  // 设置响应头
  res.setHeader('Content-Type', 'application/json');
  res.setHeader('X-Custom-Header', 'value');
  
  // 一次性设置状态码和头
  res.writeHead(200, {
    'Content-Type': 'application/json',
    'Cache-Control': 'no-cache'
  });
  
  // 写入响应体(可多次调用)
  res.write('Hello ');
  res.write('World');
  
  // 结束响应
  res.end();
  
  // 或者直接结束并发送数据
  res.end('Hello World');
  
  // 重定向
  res.writeHead(302, { 'Location': '/new-path' });
  res.end();
});

3. 处理 POST 请求和请求体

问题:如何处理 POST 请求和解析请求体?

答案

javascript
const http = require('http');
const querystring = require('querystring');

const server = http.createServer((req, res) => {
  if (req.method === 'POST' && req.url === '/submit') {
    let body = '';
    
    // 接收数据块
    req.on('data', chunk => {
      body += chunk.toString();
    });
    
    // 数据接收完成
    req.on('end', () => {
      // 根据 Content-Type 解析
      const contentType = req.headers['content-type'];
      
      let data;
      if (contentType === 'application/json') {
        try {
          data = JSON.parse(body);
        } catch (e) {
          res.writeHead(400);
          res.end('Invalid JSON');
          return;
        }
      } else if (contentType === 'application/x-www-form-urlencoded') {
        data = querystring.parse(body);
      } else {
        data = { raw: body };
      }
      
      console.log('Received data:', data);
      
      res.writeHead(200, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify({ success: true, received: data }));
    });
    
    // 错误处理
    req.on('error', err => {
      console.error('Request error:', err);
      res.writeHead(500);
      res.end('Server Error');
    });
  }
});

// 带大小限制的请求体处理
function handleRequestWithLimit(req, res, maxSize = 1024 * 1024) {
  let body = '';
  let bodyLength = 0;
  
  req.on('data', chunk => {
    bodyLength += chunk.length;
    
    if (bodyLength > maxSize) {
      req.pause();
      res.writeHead(413);  // Payload Too Large
      res.end('Request body too large');
      return;
    }
    
    body += chunk.toString();
  });
  
  req.on('end', () => {
    // 处理 body
  });
}

4. HTTP 客户端

问题:如何使用 HTTP 模块发送 HTTP 请求?

答案

GET 请求

javascript
const http = require('http');
const https = require('https');

// 基本 GET 请求
http.get('http://api.example.com/data', (res) => {
  let data = '';
  
  // 接收数据
  res.on('data', chunk => {
    data += chunk;
  });
  
  // 完成
  res.on('end', () => {
    console.log('Response:', data);
  });
}).on('error', err => {
  console.error('Error:', err.message);
});

// 带选项的请求
const options = {
  hostname: 'api.example.com',
  port: 80,
  path: '/users',
  method: 'GET',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer token123'
  }
};

const req = http.request(options, res => {
  console.log('Status:', res.statusCode);
  console.log('Headers:', res.headers);
  
  let data = '';
  res.on('data', chunk => data += chunk);
  res.on('end', () => console.log('Body:', data));
});

req.on('error', err => console.error('Request error:', err));
req.end();

POST 请求

javascript
const postData = JSON.stringify({
  name: 'John',
  email: 'john@example.com'
});

const options = {
  hostname: 'api.example.com',
  port: 80,
  path: '/users',
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Content-Length': Buffer.byteLength(postData)
  }
};

const req = http.request(options, res => {
  let data = '';
  res.on('data', chunk => data += chunk);
  res.on('end', () => console.log('Response:', data));
});

req.on('error', err => console.error('Error:', err));
req.write(postData);
req.end();

Promise 封装

javascript
function httpRequest(options, postData = null) {
  return new Promise((resolve, reject) => {
    const protocol = options.protocol === 'https:' ? https : http;
    
    const req = protocol.request(options, res => {
      let data = '';
      res.on('data', chunk => data += chunk);
      res.on('end', () => {
        resolve({
          statusCode: res.statusCode,
          headers: res.headers,
          body: data
        });
      });
    });
    
    req.on('error', reject);
    req.on('timeout', () => {
      req.destroy();
      reject(new Error('Request timeout'));
    });
    
    if (postData) {
      req.write(postData);
    }
    req.end();
  });
}

// 使用
const response = await httpRequest({
  hostname: 'api.example.com',
  path: '/data',
  method: 'GET'
});
console.log(response.body);

5. 文件上传处理

问题:如何处理文件上传?

答案

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 boundary = req.headers['content-type']
      .split(';')[1]
      .trim()
      .split('=')[1];
    
    let rawData = Buffer.alloc(0);
    
    req.on('data', chunk => {
      rawData = Buffer.concat([rawData, chunk]);
    });
    
    req.on('end', () => {
      // 解析 multipart/form-data
      const parts = parseMultipart(rawData, boundary);
      
      parts.forEach(part => {
        if (part.filename) {
          // 保存文件
          const filepath = path.join('uploads', part.filename);
          fs.writeFileSync(filepath, part.data);
          console.log(`File saved: ${filepath}`);
        }
      });
      
      res.writeHead(200, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify({ message: 'Upload successful' }));
    });
  }
});

function parseMultipart(data, boundary) {
  const parts = [];
  const delimiter = `--${boundary}`;
  const items = data.toString().split(delimiter);
  
  items.forEach(item => {
    if (item.includes('Content-Disposition')) {
      const lines = item.split('\r\n');
      const header = lines[1];  // Content-Disposition 行
      
      // 解析文件名
      const filenameMatch = header.match(/filename="([^"]+)"/);
      const nameMatch = header.match(/name="([^"]+)"/);
      
      if (filenameMatch) {
        // 找到数据开始的位置(空行之后)
        let dataStart = 2;
        while (lines[dataStart] !== '' && dataStart < lines.length) {
          dataStart++;
        }
        dataStart++;
        
        // 提取文件数据
        const fileData = lines.slice(dataStart, -2).join('\r\n');
        
        parts.push({
          filename: filenameMatch[1],
          name: nameMatch ? nameMatch[1] : null,
          data: Buffer.from(fileData)
        });
      }
    }
  });
  
  return parts;
}

6. HTTP 服务器高级配置

问题:如何配置 HTTP 服务器的高级选项?

答案

javascript
const http = require('http');

// 创建服务器
const server = http.createServer({
  // 请求超时
  requestTimeout: 30000,  // 30 秒
  
  // 头大小限制
  maxHeaderSize: 16384,   // 16KB
  
  // 保持连接
  keepAlive: true,
  keepAliveInitialDelay: 1000
}, (req, res) => {
  res.end('Hello');
});

// 连接超时
server.timeout = 120000;  // 2 分钟

// 头超时
server.headersTimeout = 40000;  // 40 秒

// 保持活动超时
server.keepAliveTimeout = 5000;  // 5 秒

// 最大请求数(每个连接)
server.maxRequestsPerSocket = 1000;

// 监听
server.listen(3000, '0.0.0.0', () => {
  console.log('Server listening on port 3000');
});

// 连接事件
server.on('connection', socket => {
  console.log('New connection:', socket.remoteAddress);
});

// 请求事件
server.on('request', (req, res) => {
  console.log(`${req.method} ${req.url}`);
});

// 错误处理
server.on('error', err => {
  if (err.code === 'EADDRINUSE') {
    console.error('Port already in use');
  } else {
    console.error('Server error:', err);
  }
});

// 优雅关闭
function gracefulShutdown() {
  console.log('Shutting down gracefully...');
  
  server.close(() => {
    console.log('Server closed');
    process.exit(0);
  });
  
  // 强制关闭超时
  setTimeout(() => {
    console.error('Forced shutdown');
    process.exit(1);
  }, 30000);
}

process.on('SIGTERM', gracefulShutdown);
process.on('SIGINT', gracefulShutdown);

7. HTTPS 服务器

问题:如何创建 HTTPS 服务器?

答案

javascript
const https = require('https');
const fs = require('fs');

// 读取证书
const options = {
  key: fs.readFileSync('private-key.pem'),
  cert: fs.readFileSync('certificate.pem'),
  // 可选:CA 证书
  ca: fs.readFileSync('ca-cert.pem')
};

// 创建 HTTPS 服务器
const server = https.createServer(options, (req, res) => {
  res.writeHead(200);
  res.end('Hello HTTPS World\n');
});

server.listen(443, () => {
  console.log('HTTPS Server running on port 443');
});

// HTTP 重定向到 HTTPS
const http = require('http');
http.createServer((req, res) => {
  res.writeHead(301, { 'Location': `https://${req.headers.host}${req.url}` });
  res.end();
}).listen(80);

// 自签名证书(开发环境)
const { generateKeyPairSync } = require('crypto');

function generateSelfSignedCert() {
  const { privateKey, publicKey } = generateKeyPairSync('rsa', {
    modulusLength: 2048,
    publicKeyEncoding: { type: 'spki', format: 'pem' },
    privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
  });
  
  // 注意:实际使用需要完整的证书生成流程
  return { key: privateKey, cert: publicKey };
}

8. HTTP 代理服务器

问题:如何实现一个简单的 HTTP 代理服务器?

答案

javascript
const http = require('http');
const https = require('https');
const url = require('url');

const proxy = http.createServer((req, res) => {
  console.log(`Proxying: ${req.method} ${req.url}`);
  
  // 解析目标 URL
  const targetUrl = url.parse(req.url);
  
  // 代理选项
  const options = {
    hostname: targetUrl.hostname,
    port: targetUrl.port || 80,
    path: targetUrl.path,
    method: req.method,
    headers: req.headers
  };
  
  // 创建到目标服务器的请求
  const proxyReq = http.request(options, proxyRes => {
    // 将目标响应转发给客户端
    res.writeHead(proxyRes.statusCode, proxyRes.headers);
    proxyRes.pipe(res);
  });
  
  // 错误处理
  proxyReq.on('error', err => {
    console.error('Proxy error:', err);
    res.writeHead(502);
    res.end('Bad Gateway');
  });
  
  // 将客户端请求体转发给目标
  req.pipe(proxyReq);
});

proxy.listen(8080, () => {
  console.log('Proxy server listening on port 8080');
});

// HTTPS 代理
const httpsProxy = http.createServer((req, res) => {
  const targetUrl = url.parse(`https://${req.url}`);
  
  const options = {
    hostname: targetUrl.hostname,
    port: 443,
    path: targetUrl.path,
    method: req.method,
    headers: req.headers
  };
  
  const proxyReq = https.request(options, proxyRes => {
    res.writeHead(proxyRes.statusCode, proxyRes.headers);
    proxyRes.pipe(res);
  });
  
  proxyReq.on('error', err => {
    res.writeHead(502);
    res.end('Bad Gateway');
  });
  
  req.pipe(proxyReq);
});

9. WebSocket 升级

问题:如何在 HTTP 服务器中处理 WebSocket 升级?

答案

javascript
const http = require('http');
const crypto = require('crypto');

// WebSocket 接受键的魔法字符串
const WEBSOCKET_MAGIC_STRING = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';

const server = http.createServer((req, res) => {
  res.end('HTTP Server');
});

// 处理升级请求
server.on('upgrade', (req, socket, head) => {
  // 验证 WebSocket 握手
  const key = req.headers['sec-websocket-key'];
  
  if (!key) {
    socket.destroy();
    return;
  }
  
  // 计算接受键
  const acceptKey = crypto
    .createHash('sha1')
    .update(key + WEBSOCKET_MAGIC_STRING)
    .digest('base64');
  
  // 发送握手响应
  const responseHeaders = [
    'HTTP/1.1 101 Switching Protocols',
    'Upgrade: websocket',
    'Connection: Upgrade',
    `Sec-WebSocket-Accept: ${acceptKey}`,
    ''
  ];
  
  socket.write(responseHeaders.join('\r\n') + '\r\n');
  
  // 处理 WebSocket 帧
  socket.on('data', data => {
    const message = decodeWebSocketFrame(data);
    console.log('Received:', message);
    
    // 发送响应
    socket.write(encodeWebSocketFrame('Echo: ' + message));
  });
  
  socket.on('end', () => {
    console.log('Client disconnected');
  });
});

// 解码 WebSocket 帧
function decodeWebSocketFrame(buffer) {
  const firstByte = buffer[0];
  const secondByte = buffer[1];
  
  const opcode = firstByte & 0x0f;
  const masked = (secondByte & 0x80) !== 0;
  let payloadLength = secondByte & 0x7f;
  
  let offset = 2;
  
  if (payloadLength === 126) {
    payloadLength = buffer.readUInt16BE(2);
    offset = 4;
  } else if (payloadLength === 127) {
    payloadLength = buffer.readUInt32BE(2);
    offset = 10;
  }
  
  let maskingKey;
  if (masked) {
    maskingKey = buffer.slice(offset, offset + 4);
    offset += 4;
  }
  
  const payload = buffer.slice(offset, offset + payloadLength);
  
  if (masked) {
    for (let i = 0; i < payload.length; i++) {
      payload[i] ^= maskingKey[i % 4];
    }
  }
  
  return payload.toString('utf8');
}

// 编码 WebSocket 帧
function encodeWebSocketFrame(message) {
  const payload = Buffer.from(message, 'utf8');
  const payloadLength = payload.length;
  
  let frame;
  
  if (payloadLength < 126) {
    frame = Buffer.allocUnsafe(2);
    frame[0] = 0x81;  // FIN=1, opcode=text
    frame[1] = payloadLength;
  } else if (payloadLength < 65536) {
    frame = Buffer.allocUnsafe(4);
    frame[0] = 0x81;
    frame[1] = 126;
    frame.writeUInt16BE(payloadLength, 2);
  } else {
    frame = Buffer.allocUnsafe(10);
    frame[0] = 0x81;
    frame[1] = 127;
    frame.writeUInt32BE(payloadLength, 2);
  }
  
  return Buffer.concat([frame, payload]);
}

server.listen(3000);

10. HTTP 性能优化

问题:如何优化 HTTP 服务器的性能?

答案

javascript
const http = require('http');
const cluster = require('cluster');
const os = require('os');

// 集群模式
if (cluster.isMaster) {
  const numCPUs = os.cpus().length;
  
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }
  
  cluster.on('exit', (worker) => {
    console.log(`Worker ${worker.process.pid} died`);
    cluster.fork();
  });
} else {
  // 创建服务器
  const server = http.createServer((req, res) => {
    // 启用压缩
    const acceptEncoding = req.headers['accept-encoding'] || '';
    
    if (acceptEncoding.includes('gzip')) {
      res.setHeader('Content-Encoding', 'gzip');
      // 使用 zlib 压缩响应
    }
    
    // 设置缓存头
    res.setHeader('Cache-Control', 'public, max-age=3600');
    res.setHeader('ETag', '"abc123"');
    
    res.end('Hello World');
  });
  
  // 性能配置
  server.maxHeadersCount = 2000;
  server.requestTimeout = 30000;
  server.keepAliveTimeout = 5000;
  
  server.listen(3000);
  console.log(`Worker ${process.pid} started`);
}

// 连接池(HTTP Agent)
const agent = new http.Agent({
  keepAlive: true,
  maxSockets: 50,
  maxFreeSockets: 10,
  timeout: 30000,
  freeSocketTimeout: 30000
});

// 使用连接池发送请求
http.get({
  hostname: 'api.example.com',
  path: '/data',
  agent: agent  // 使用自定义 agent
}, res => {
  // 处理响应
});

// 流式响应(大文件)
const fs = require('fs');

http.createServer((req, res) => {
  if (req.url === '/large-file') {
    const stream = fs.createReadStream('large-file.zip');
    
    res.writeHead(200, {
      'Content-Type': 'application/zip',
      'Content-Disposition': 'attachment; filename="file.zip"'
    });
    
    stream.pipe(res);
    
    stream.on('error', err => {
      console.error('Stream error:', err);
      res.end();
    });
  }
});