手写支持静态文件与路由的 HTTP 服务器
本节目标
- 用 Node
http模块手写一个服务器 - 实现静态文件服务与简单路由
- 理解 Koa/Express 的"请求-响应"模型本质
最小服务器:http.createServer 每来一个请求调用一次回调,我们在回调里读 URL、组织响应。
// 运行环境:Node.js 14+
// 保存为 aj-l3.js,执行:node aj-l3.js,浏览器访问 http://localhost:3000/
const http = require('http')
const fs = require('fs')
const path = require('path')
const MIME = { '.html': 'text/html', '.js': 'text/javascript', '.css': 'text/css', '.json': 'application/json' }
const server = http.createServer((req, res) => {
// 路由:API 与静态文件分流
if (req.url === '/api/hello') {
res.writeHead(200, { 'Content-Type': 'application/json' })
return res.end(JSON.stringify({ msg: 'hello from handmade server' }))
}
// 静态文件:映射到 ./public 目录(防目录穿越)
const safe = path.normalize(req.url).replace(/^(\.\.[/\\])+/, '')
const file = path.join(__dirname, 'public', safe === '/' ? 'index.html' : safe)
fs.readFile(file, (err, data) => {
if (err) { res.writeHead(404); return res.end('Not Found') }
res.writeHead(200, { 'Content-Type': MIME[path.extname(file)] || 'application/octet-stream' })
res.end(data)
})
})
server.listen(3000, () => console.log('server on http://localhost:3000'))
// 调用示例(另开一个终端用 curl 测试):
// curl http://localhost:3000/api/hello
// curl http://localhost:3000/ (需有 public/index.html)关键点:res.writeHead 写状态码与响应头,res.end(data) 结束响应并发送 body。路由本质就是"根据 req.url/req.method 分发到不同处理函数"——这正是 Express/Koa 路由的核心。
名词解释
- HTTP 服务器(http.Server):监听端口、收到请求触发回调的程序。
req是请求对象(含 url/method/headers),res是响应对象(writeHead/end 写回)。 - MIME 类型:告诉浏览器"返回的字节是什么格式"(html/js/css/json…),写错会导致浏览器拒执行脚本或乱展示。
- 目录穿越(Path Traversal):攻击者用
../../访问 web 根目录外的文件。path.normalize+ 去除../前缀是基本防护。
课后练习
- 为什么静态服务必须做"目录穿越"防护?
- 答案:若不处理
../,用户请求/../../etc/passwd可能读到系统敏感文件;path.normalize解析后剥掉../前缀,把所有路径锁在public内。
- 答案:若不处理
res.end和res.write有什么区别?- 答案:
end发送数据并结束响应(只能调一次);write可分多次发送数据但响应仍打开,最后仍需end关闭。
- 答案:
总结
手写一个 HTTP 服务器,是前端工程师"向上够一够后端"性价比最高的一课。你会发现 Express、Koa、甚至 Nest,本质上都是 http.createServer 回调里那套"读 URL → 分发处理函数 → 组织响应"的封装。理解了这条主线,你就不会被框架的"中间件""路由表"绕晕——它们只是让分发更优雅、让错误处理更统一。我特别想提醒两个工程常识:第一,MIME 类型写错会让浏览器把 JS 当文本、把 JSON 当下载,看似玄学实则低级错误;第二,任何文件服务都必须堵死目录穿越,这是底线安全。亲手跑通这个服务器,你对"前后端边界"的理解会从"调接口"升级到"我也能造接口"。