Node.js HTTP 服务

Node 自带几十个内置模块,不依赖任何第三方包就能干活。最经典的就是 http——用它几行代码就能起一个 HTTP 服务器。这是 Express、Koa、Fastify 等所有 Web 框架的底层,理解了它,你就理解了所有 Node Web 服务的本质。

1. 最简单的 HTTP 服务器

下面这段代码起了一个 HTTP 服务,浏览器访问就能看到响应。把它保存为 server.js,运行 node server.js:

const http = require("http");

// createServer 接收回调,每个请求都会调一次
const server = http.createServer((req, res) => {
  // req(IncomingMessage):请求对象,有 url、method、headers
  // res(ServerResponse):响应对象,用来写回响应
  res.writeHead(200, { "Content-Type": "text/plain" });
  res.end("Hello, World!");
});

// 监听 3000 端口
server.listen(3000, () => {
  console.log("服务器跑在 http://localhost:3000");
});

// 浏览器访问 http://localhost:3000 就能看到 "Hello, World!"

几个要点:

2. req 和 res 对象

所有 HTTP 服务器的核心都是围绕这两个对象:从 req 读请求信息,往 res 写响应。req 常用属性:

res 常用方法:

3. 路由分发

真实服务器的核心逻辑就是"根据 URL 和方法,把请求分发到不同的处理函数"。原生 httpif/else 就能做:

const http = require("http");

const server = http.createServer((req, res) => {
  const url = req.url;        // 如 "/users/42"
  const method = req.method;  // 如 "GET" "POST"

  // 用 if/else 做路由分发
  if (url === "/" && method === "GET") {
    res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
    res.end("<h1>首页</h1>");

  } else if (url === "/api/time") {
    res.writeHead(200, { "Content-Type": "application/json" });
    res.end(JSON.stringify({ now: Date.now() }));

  } else if (url.startsWith("/users/") && method === "GET") {
    // 解析路径参数:/users/42 → id = 42
    const id = url.split("/")[2];
    res.writeHead(200, { "Content-Type": "application/json" });
    res.end(JSON.stringify({ id, name: "用户" + id }));

  } else {
    res.writeHead(404, { "Content-Type": "text/plain" });
    res.end("Not Found");
  }
});

server.listen(3000, () => console.log("http://localhost:3000"));

这就是所有 Web 框架的本质——Express 的 app.get("/users/:id", ...) 只是把这个 if/else 包装得更优雅。理解了原生路由,你再看任何框架都一目了然。

4. 处理 POST 请求体

GET 请求的参数在 URL 里(?name=abc),但 POST 的数据在请求体里。Node 不会自动解析请求体——它通过流(stream)的方式分块到达,你需要监听 dataend 事件自己拼接:

const http = require("http");

const server = http.createServer((req, res) => {
  if (req.url === "/api/users" && req.method === "POST") {
    // POST 数据通过"流"分块到达,要自己拼接
    let body = "";
    req.on("data", chunk => {
      body += chunk.toString();   // chunk 是 Buffer,转字符串
    });
    req.on("end", () => {
      // 数据接收完毕,解析 JSON
      const user = JSON.parse(body);
      console.log("收到:", user);
      res.writeHead(201, { "Content-Type": "application/json" });
      res.end(JSON.stringify({ ok: true, id: Date.now() }));
    });
  } else {
    res.writeHead(404);
    res.end();
  }
});

server.listen(3000);

测试 POST 接口用 curl:

# 用 curl 发 POST 请求
curl -X POST http://localhost:3000/api/users \
  -H "Content-Type: application/json" \
  -d '{"name":"小明","age":20}'

# 响应: {"ok":true,"id":1690000000000}

注意:大文件上传时 body 可能很大,要做大小限制,否则会被人恶意撑爆内存。框架(Express 的 body-parser)都内置了这些保护,原生写要自己注意。

5. 请求头与响应头

HTTP 头是请求/响应的"元数据"——Content-TypeAuthorizationCookieUser-Agent 都在这里:

const server = http.createServer((req, res) => {
  // 读请求头
  const userAgent = req.headers["user-agent"];
  const auth = req.headers["authorization"];
  console.log("UA:", userAgent);

  // 写响应头(必须在 end 之前)
  res.setHeader("Content-Type", "application/json");
  res.setHeader("X-Custom-Header", "hello");
  // 或一次性写多个
  res.writeHead(200, {
    "Content-Type": "application/json",
    "Cache-Control": "no-cache"
  });

  // 写响应体(end 也可以带数据,等价于 write + end)
  res.write("第一部分");
  res.write("第二部分");
  res.end("结束");
});

几个高频头:

6. 静态文件服务器

把 HTML、CSS、图片、JS 文件提供给浏览器,是 Web 服务最基础的需求。配合 fs 模块,几十行就能写一个:

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 ext = path.extname(filePath);
  const types = {
    ".html": "text/html",
    ".css": "text/css",
    ".js": "text/javascript",
    ".png": "image/png",
    ".jpg": "image/jpeg"
  };

  fs.readFile(filePath, (err, data) => {
    if (err) {
      res.writeHead(404);
      res.end("文件不存在");
      return;
    }
    res.writeHead(200, { "Content-Type": types[ext] || "application/octet-stream" });
    res.end(data);
  });
});

server.listen(3000, () => console.log("静态服务 http://localhost:3000"));

这个例子把当前目录变成一个静态站点。生产环境一般不会自己写——用 Express 的 express.static 或 Nginx 都更高效安全。但理解原理很重要。

7. 端口与监听

listen(port, callback) 让服务器监听指定端口。几个常识:

小结

原生 http 模块揭示了所有 Node Web 服务的本质:收到请求 → 根据 URL/方法分发 → 读请求、写响应 → end 结束。框架只是把这层分发做得更优雅、把常用功能(参数解析、错误处理、日志)封装好。下一篇我们看 Node 另一个核心模块 events——事件驱动的基石。

← 上一篇 Node.js 模块系统

下一篇 Node.js 事件

✈️💬