Skip to content

Node.js 网络与 HTTP

Node.js 内置 http 模块即可构建 Web 服务,理解原生 HTTP 编程有助于掌握 Express / Koa / NestJS 等框架的底层原理。

作者:yanshaodong

创建 HTTP 服务

javascript
const http = require('http')

const server = http.createServer((req, res) => {
  // req:可读流,包含请求行/头/体
  // res:可写流,用于返回响应
  res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' })
  res.end('Hello')
})

server.listen(3000, () => console.log('listening on 3000'))

请求对象(req)

javascript
const { URL } = require('url')

server.on('request', (req, res) => {
  const url = new URL(req.url, 'http://' + req.headers.host)

  console.log(req.method)        // GET / POST ...
  console.log(url.pathname)      // /api/users
  console.log(req.headers)       // 请求头对象

  // 读取请求体(流式)
  let body = ''
  req.on('data', (chunk) => { body += chunk })
  req.on('end', () => {
    console.log('body:', body)
  })
})

响应对象(res)

javascript
res.statusCode = 201
res.setHeader('Content-Type', 'application/json')
res.write(JSON.stringify({ ok: true }))
res.end()                       // 结束响应(必须调用)

// 或一次性
res.writeHead(404, { 'Content-Type': 'text/plain' })
res.end('Not Found')

路由分发

javascript
const server = http.createServer((req, res) => {
  const { pathname } = new URL(req.url, 'http://localhost')

  if (req.method === 'GET' && pathname === '/api/users') {
    res.end(JSON.stringify([{ id: 1, name: 'Tom' }]))
  } else if (req.method === 'POST' && pathname === '/api/users') {
    // 处理创建
    res.end('created')
  } else {
    res.statusCode = 404
    res.end('Not Found')
  }
})

静态文件服务

javascript
const fs = require('fs')
const path = require('path')
const mime = require('mime-types')  // npm i mime-types

server.on('request', (req, res) => {
  const filePath = path.join(__dirname, 'public', req.url)
  fs.createReadStream(filePath)
    .on('error', () => { res.statusCode = 404; res.end('404') })
    .pipe(res)
})

中间件雏形

框架的"中间件"本质是对 req/res 的链式处理函数

javascript
const middlewares = []

function use(fn) { middlewares.push(fn) }

use((req, res, next) => {
  req.start = Date.now()
  next()
})

use((req, res, next) => {
  console.log('耗时:', Date.now() - req.start)
  next()
})

function handle(req, res) {
  let i = 0
  const next = () => {
    const fn = middlewares[i++]
    fn ? fn(req, res, next) : res.end('done')
  }
  next()
}

这正是 Express/Koa 的设计思想:把通用逻辑(日志、鉴权、错误处理)拆成可复用的中间件。

HTTPS 服务

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

const options = {
  key: fs.readFileSync('privkey.pem'),
  cert: fs.readFileSync('fullchain.pem')
}

https.createServer(options, (req, res) => {
  res.end('secure')
}).listen(443)

客户端请求

javascript
const https = require('https')

https.get('https://api.example.com/data', (res) => {
  let data = ''
  res.on('data', (c) => (data += c))
  res.on('end', () => console.log(JSON.parse(data)))
})

实际项目中建议直接使用 fetch(Node 18+ 内置)、axiosundici,而非手写 http.request


作者:yanshaodong