Node.js HTTP 服务器基础

核心知识点

HTTP 模块简介

http 模块是 Node.js 的核心模块之一,它提供了创建 HTTP 服务器和客户端的功能。通过 http 模块,我们可以:

  • 创建 HTTP 服务器
  • 处理 HTTP 请求
  • 发送 HTTP 响应
  • 创建 HTTP 客户端

创建 HTTP 服务器

使用 http.createServer() 方法创建 HTTP 服务器:

const http = require('http');

const server = http.createServer((req, res) => {
  // 处理请求和发送响应
});

server.listen(port, hostname, () => {
  console.log(`服务器运行在 http://${hostname}:${port}/`);
});

请求对象 (req)

req 对象代表 HTTP 请求,它包含了请求的信息:

  • req.url:请求路径
  • req.method:请求方法(GET, POST, PUT, DELETE 等)
  • req.headers:请求头
  • req.httpVersion:HTTP 版本

响应对象 (res)

res 对象代表 HTTP 响应,它用于发送响应给客户端:

  • res.writeHead(statusCode, headers):设置响应头
  • res.write(data):发送响应体数据
  • res.end(data):结束响应并发送数据
  • res.statusCode:设置响应状态码

服务器监听

使用 server.listen() 方法启动服务器并监听指定的端口:

server.listen(port, hostname, backlog, callback);

实用案例

案例一:创建简单的 HTTP 服务器

const http = require('http');

const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer((req, res) => {
  // 设置响应状态码和响应头
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  
  // 发送响应体并结束响应
  res.end('Hello, Node.js HTTP Server!\n');
});

server.listen(port, hostname, () => {
  console.log(`服务器运行在 http://${hostname}:${port}/`);
});

案例二:根据请求路径返回不同内容

const http = require('http');

const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer((req, res) => {
  // 设置响应头
  res.setHeader('Content-Type', 'text/plain');
  
  // 根据请求路径返回不同内容
  switch (req.url) {
    case '/':
      res.statusCode = 200;
      res.end('欢迎访问首页!\n');
      break;
    case '/about':
      res.statusCode = 200;
      res.end('关于我们\n');
      break;
    case '/contact':
      res.statusCode = 200;
      res.end('联系我们\n');
      break;
    default:
      res.statusCode = 404;
      res.end('404 Not Found\n');
      break;
  }
});

server.listen(port, hostname, () => {
  console.log(`服务器运行在 http://${hostname}:${port}/`);
});

案例三:处理不同的 HTTP 请求方法

const http = require('http');

const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer((req, res) => {
  // 设置响应头
  res.setHeader('Content-Type', 'text/plain');
  
  // 根据请求方法和路径处理请求
  if (req.url === '/api/data') {
    switch (req.method) {
      case 'GET':
        res.statusCode = 200;
        res.end('GET 请求:获取数据\n');
        break;
      case 'POST':
        res.statusCode = 200;
        res.end('POST 请求:创建数据\n');
        break;
      case 'PUT':
        res.statusCode = 200;
        res.end('PUT 请求:更新数据\n');
        break;
      case 'DELETE':
        res.statusCode = 200;
        res.end('DELETE 请求:删除数据\n');
        break;
      default:
        res.statusCode = 405;
        res.setHeader('Allow', 'GET, POST, PUT, DELETE');
        res.end('方法不允许\n');
        break;
    }
  } else {
    res.statusCode = 404;
    res.end('404 Not Found\n');
  }
});

server.listen(port, hostname, () => {
  console.log(`服务器运行在 http://${hostname}:${port}/`);
});

案例四:读取并返回 HTML 文件

const http = require('http');
const fs = require('fs');
const path = require('path');

const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer((req, res) => {
  // 处理根路径请求
  if (req.url === '/' || req.url === '/index.html') {
    // 读取 HTML 文件
    const filePath = path.join(__dirname, 'index.html');
    
    fs.readFile(filePath, 'utf8', (err, data) => {
      if (err) {
        res.statusCode = 500;
        res.setHeader('Content-Type', 'text/plain');
        res.end('服务器内部错误\n');
        return;
      }
      
      // 返回 HTML 内容
      res.statusCode = 200;
      res.setHeader('Content-Type', 'text/html');
      res.end(data);
    });
  } else {
    res.statusCode = 404;
    res.setHeader('Content-Type', 'text/plain');
    res.end('404 Not Found\n');
  }
});

server.listen(port, hostname, () => {
  console.log(`服务器运行在 http://${hostname}:${port}/`);
});

案例五:解析 URL 查询参数

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

const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer((req, res) => {
  // 解析 URL
  const parsedUrl = url.parse(req.url, true);
  const pathname = parsedUrl.pathname;
  const query = parsedUrl.query;
  
  res.setHeader('Content-Type', 'text/plain');
  
  if (pathname === '/greet') {
    // 获取查询参数中的名字
    const name = query.name || 'World';
    
    res.statusCode = 200;
    res.end(`Hello, ${name}!\n`);
  } else if (pathname === '/add') {
    // 计算两个数的和
    const a = parseInt(query.a) || 0;
    const b = parseInt(query.b) || 0;
    const sum = a + b;
    
    res.statusCode = 200;
    res.end(`Sum of ${a} and ${b} is ${sum}\n`);
  } else {
    res.statusCode = 404;
    res.end('404 Not Found\n');
  }
});

server.listen(port, hostname, () => {
  console.log(`服务器运行在 http://${hostname}:${port}/`);
});

学习目标

  1. 理解 HTTP 服务器概念:掌握 HTTP 服务器的基本原理和工作机制
  2. 使用 http 模块:学会使用 Node.js 的 http 模块创建 HTTP 服务器
  3. 处理 HTTP 请求:能够处理不同路径和方法的 HTTP 请求
  4. 发送 HTTP 响应:学会设置响应头和发送响应体
  5. 读取静态文件:能够读取并返回 HTML 等静态文件
  6. 解析 URL 参数:学会解析 URL 中的查询参数

代码优化建议

1. 合理组织服务器代码

不好的做法

const http = require('http');

const server = http.createServer((req, res) => {
  // 所有逻辑都在一个回调函数中
  if (req.url === '/') {
    // 处理根路径
  } else if (req.url === '/about') {
    // 处理 about 路径
  } else if (req.url === '/contact') {
    // 处理 contact 路径
  } else {
    // 处理 404
  }
});

server.listen(3000);

好的做法

const http = require('http');

// 处理不同路径的函数
function handleRoot(req, res) {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World!\n');
}

function handleAbout(req, res) {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('About Page\n');
}

function handleContact(req, res) {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Contact Page\n');
}

function handleNotFound(req, res) {
  res.statusCode = 404;
  res.setHeader('Content-Type', 'text/plain');
  res.end('404 Not Found\n');
}

// 服务器回调函数
const server = http.createServer((req, res) => {
  switch (req.url) {
    case '/':
      handleRoot(req, res);
      break;
    case '/about':
      handleAbout(req, res);
      break;
    case '/contact':
      handleContact(req, res);
      break;
    default:
      handleNotFound(req, res);
      break;
  }
});

server.listen(3000, () => {
  console.log('服务器运行在 http://127.0.0.1:3000/');
});

2. 错误处理

不好的做法

const http = require('http');
const fs = require('fs');

const server = http.createServer((req, res) => {
  fs.readFile('index.html', 'utf8', (err, data) => {
    // 没有错误处理
    res.end(data);
  });
});

server.listen(3000);

好的做法

const http = require('http');
const fs = require('fs');

const server = http.createServer((req, res) => {
  fs.readFile('index.html', 'utf8', (err, data) => {
    if (err) {
      console.error('读取文件错误:', err);
      res.statusCode = 500;
      res.setHeader('Content-Type', 'text/plain');
      res.end('服务器内部错误\n');
      return;
    }
    res.statusCode = 200;
    res.setHeader('Content-Type', 'text/html');
    res.end(data);
  });
});

server.listen(3000, () => {
  console.log('服务器运行在 http://127.0.0.1:3000/');
});

3. 使用 URL 模块解析路径

不好的做法

const http = require('http');

const server = http.createServer((req, res) => {
  // 手动解析路径和查询参数
  const parts = req.url.split('?');
  const pathname = parts[0];
  const queryString = parts[1] || '';
  const query = {};
  
  if (queryString) {
    queryString.split('&').forEach(pair => {
      const [key, value] = pair.split('=');
      query[key] = decodeURIComponent(value);
    });
  }
  
  // 处理请求
});

server.listen(3000);

好的做法

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

const server = http.createServer((req, res) => {
  // 使用 url 模块解析
  const parsedUrl = url.parse(req.url, true);
  const pathname = parsedUrl.pathname;
  const query = parsedUrl.query;
  
  // 处理请求
});

server.listen(3000);

常见问题与解决方案

问题1:服务器启动失败

原因

  • 端口被占用
  • 权限不足(使用低于 1024 的端口)

解决方案

  • 更改端口号
  • 使用 sudo 命令(仅在必要时)

问题2:静态文件返回乱码

原因

  • 没有设置正确的字符编码

解决方案

  • 在读取文件时指定编码
  • 设置正确的 Content-Type 响应头

问题3:请求路径匹配问题

原因

  • 路径匹配不精确
  • 没有考虑查询参数

解决方案

  • 使用 url.parse() 解析路径
  • 只匹配 pathname 部分

问题4:服务器崩溃

原因

  • 未捕获的错误
  • 异步操作中的错误

解决方案

  • 添加错误处理
  • 使用 try/catch 捕获同步错误
  • 在回调函数中处理异步错误

总结

通过本教程的学习,你应该能够:

  1. 理解 HTTP 服务器的基本概念和工作原理
  2. 使用 Node.js 的 http 模块创建简单的 HTTP 服务器
  3. 处理不同路径和方法的 HTTP 请求
  4. 发送带有正确响应头的 HTTP 响应
  5. 读取并返回静态文件
  6. 解析 URL 中的查询参数

HTTP 服务器是 Node.js 的核心功能之一,掌握这些基础知识对于学习更高级的 Web 框架(如 Express)非常重要。在后续章节中,我们将学习如何使用 Express 框架来更方便地创建和管理 HTTP 服务器。

« 上一篇 Node.js async/await 语法 下一篇 » Node.js HTTP 服务器进阶