对于Node.js新手,搭建一个静态资源服务器是个不错的锻炼,从最简单的返回文件或错误开始,渐进增强,还可以逐步加深对http的理解。那就开始吧,让我们的双手沾满网络请求!
Note:
当然在项目中如果有使用express框架,用express.static一行代码就可以达到目的了:
app.use(express.static('public'))
这里我们要实现的正是express.static背后所做工作的一部分,建议同步阅读该模块源码。
基本功能
不急着写下第一行代码,而是先梳理一下就基本功能而言有哪些步骤。
在本地根据指定端口启动一个http server,等待着来自客户端的请求
当请求抵达时,根据请求的url,以设置的静态文件目录为base,映射得到文件位置
检查文件是否存在
如果文件不存在,返回404状态码,发送not found页面到客户端
如果文件存在:
打开文件待读取
设置response header
发送文件到客户端
6.等待来自客户端的下一个请求
实现基本功能
代码结构
创建一个nodejs-static-webserver目录,在目录内运行npm init初始化一个package.json文件。
mkdir nodejs-static-webserver && cd "$_"
// initialize package.json
npm init
接着创建如下文件目录:
-- config ---- default.json -- static-server.js -- app.js default.json { "port": 9527, "root": "/Users/sheila1227/Public", "indexPage": "index.html" }
default.js存放一些默认配置,比如端口号、静态文件目录(root)、默认页(indexPage)等。当这样的一个请求:9527/myfiles/抵达时. 如果根据root映射后得到的目录内有index.html,根据我们的默认配置,就会给客户端发回index.html的内容。
static-server.js
const http = require('http'); const path = require('path'); const config = require('./config/default'); class StaticServer { constructor() { this.port = config.port; this.root = config.root; this.indexPage = config.indexPage; } start() { http.createServer((req, res) => { const pathName = path.join(this.root, path.normalize(req.url)); res.writeHead(200); res.end(`Requeste path: ${pathName}`); }).listen(this.port, err => { if (err) { console.error(err); console.info('Failed to start server'); } else { console.info(`Server started on port ${this.port}`); } }); } } module.exports = StaticServer;
在这个模块文件内,我们声明了一个StaticServer类,并给其定义了start方法,在该方法体内,创建了一个server对象,监听rquest事件,并将服务器绑定到配置文件指定的端口。在这个阶段,我们对于任何请求都暂时不作区分地简单地返回请求的文件路径。path模块用来规范化连接和解析路径,这样我们就不用特意来处理操作系统间的差异。
app.js
const StaticServer = require('./static-server'); (new StaticServer()).start();
在这个文件内,调用上面的static-server模块,并创建一个StaticServer实例,调用其start方法,启动了一个静态资源服务器。这个文件后面将不需要做其他修改,所有对静态资源服务器的完善都发生在static-server.js内。
在目录下启动程序会看到成功启动的log:
> node app.js Server started on port 9527
在浏览器中访问,可以看到服务器将请求路径直接返回了。
路由处理
之前我们对任何请求都只是向客户端返回文件位置而已,现在我们将其替换成返回真正的文件:
routeHandler(pathName, req, res) {
}
start() {
http.createServer((req, res) => {
const pathName = path.join(this.root, path.normalize(req.url));
this.routeHandler(pathName, req, res);
}).listen(this.port, err => {
...
});
}
将由routeHandler来处理文件发送。
读取静态文件
读取文件之前,用fs.stat检测文件是否存在,如果文件不存在,回调函数会接收到错误,发送404响应.
respondNotFound(req, res) { res.writeHead(404, { 'Content-Type': 'text/html' }); res.end(`<h1>Not Found</h1><p>The requested URL ${req.url} was not found on this server.</p>`); } respondFile(pathName, req, res) { const readStream = fs.createReadStream(pathName); readStream.pipe(res); } routeHandler(pathName, req, res) { fs.stat(pathName, (err, stat) => { if (!err) { this.respondFile(pathName, req, res); } else { this.respondNotFound(req, res); } }); }
Note: