本文详解如何使用Node.js搭建一个具备完整功能的静态Web服务器。通过整合http、url、fs和path模块,实现HTML页面展示、JS/CSS等静态资源直接访问、图片显示及二进制文件下载。重点解析中文路径解码、目录索引生成以及非斜杠路径的301重定向逻辑,提供约140行核心代码及运行指南。
Node.js Web服务器搭建概述
利用Node.js构建Web服务器是掌握该技术的综合性入门实践。实现一个基础的Web服务器涉及多个关键模块的应用,包括处理HTTP协议的模块、文件系统操作、URL解析、路径处理以及301重定向机制。下文将简要阐述搭建简易Web服务器的具体步骤。
Web服务器核心功能需求
一个合格的Web服务器需要满足以下基本功能需求:
1、支持渲染以.html或.htm为后缀的Web页面;
2、直接输出以.js、.css、.json或.text为后缀的文件内容;
3、正确展示图片资源;
4、自动触发以.apk、.docx或.zip为后缀的文件下载;
5、当访问路径形如http://xxx.com/a/b/时,系统应优先查找b目录下是否存在index.html文件。若存在则直接显示;若不存在,则列出该目录下的所有文件和文件夹,并允许用户进一步访问子目录或文件。
6、当访问路径形如http://xxx.com/a/b(无末尾斜杠)时,执行301重定向至http://xxx.com/a/b/。此举旨在解决内部资源引用路径错位的问题。
首先引入实现上述功能所需的各个模块:
关键模块引入
//http协议模块
var http = require('http');
//url解析模块
var url = require('url');
//文件系统模块
var fs = require("fs");
//路径解析模块
var path = require("path");
创建HTTP服务并在指定端口进行监听:
服务创建与端口监听
//创建一个服务
var httpServer = http.createServer(this.processRequest.bind(this));
//在指定的端口监听服务
httpServer.listen(port,function(){
console.log("[HttpServer][Start]","runing at http://"+ip+":"+port+"/");
console.timeEnd("[HttpServer][Start]");
});
在服务创建过程中,需传递一个名为processRequest的匿名函数以处理请求。该函数接收两个参数:request和response。request对象封装了客户端请求的所有信息,而response对象用于设置响应头并向客户端返回数据。
请求处理逻辑详解
processRequest:function(request,response){
var hasExt = true;
var requestUrl = request.url;
var pathName = url.parse(requestUrl).pathname;
//对请求的路径进行解码,防止中文乱码
pathName = decodeURI(pathName);
//如果路径中没有扩展名
if(path.extname(pathName) === ''){
//如果不是以/结尾的,加/并作301重定向
if (pathName.charAt(pathName.length-1) != "/"){
pathName += "/";
var redirect = "http://"+request.headers.host + pathName;
response.writeHead(301, {
location:redirect
});
response.end();
}
//添加默认的访问页面,但这个页面不一定存在,后面会处理
pathName += "index.html";
hasExt = false; //标记默认页面是程序自动添加的
}
//获取资源文件的相对路径
var filePath = path.join("http/webroot",pathName);
//获取对应文件的文档类型
var contentType = this.getContentType(filePath);
//如果文件名存在
fs.exists(filePath,function(exists){
if(exists){
response.writeHead(200, {"content-type":contentType});
var stream = fs.createReadStream(filePath,{flags:"r",encoding:null});
stream.on("error", function() {
response.writeHead(500,{"content-type": "text/html"});
response.end("<h1>500 Server Error</h1>");
});
//返回文件内容
stream.pipe(response);
}else { //文件名不存在的情况
if(hasExt){
//如果这个文件不是程序自动添加的,直接返回404
response.writeHead(404, {"content-type": "text/html"});
response.end("<h1>404 Not Found</h1>");
}else {
//如果文件是程序自动添加的且不存在,则表示用户希望访问的是该目录下的文件列表
var html = "<head><meta charset='utf-8'></head>";
try{
//用户访问目录
var filedir = filePath.substring(0,filePath.lastIndexOf('\\'));
//获取用户访问路径下的文件列表
var files = fs.readdirSync(filedir);
//将访问路径下的所以文件一一列举出来,并添加超链接,以便用户进一步访问
for(var i in files){
var filename = files[i];
html += "<div><a href='"+filename+"'>"+filename+"</a></div>";
}
}catch (e){
html += "<h1>您访问的目录不存在</h1>"
}
response.writeHead(200, {"content-type": "text/html"});
response.end(html);
}
}
});
},
在请求处理函数中,有几个关键逻辑需要特别注意:
针对路径中包含中文的情况,浏览器会自动对中文进行编码(英文字符保持不变)。因此,在接收到请求地址后,必须对路径进行解码处理,否则解析出的路径将与实际文件路径不一致。
若访问路径既不以具体文件后缀结尾,也不以斜杠/结尾,则需通过重定向添加末尾斜杠,以标识当前目录。若不进行此处理,当前路径下的静态资源可能无法被正确引用。
当访问路径指向一个目录时,系统应列出该目录下的所有文件和文件夹供用户点击访问。为确保中文目录名能正常显示,需在响应头中设置charset=utf-8。
代码获取与运行指南
核心代码量约为140行左右,完整代码已托管至Git仓库:https://github.com/git-onepixel/Node
如需运行演示Demo,请打开命令行工具(cmd)并切换至项目根目录,执行命令node start即可启动服务。
以上内容即为本文的全部讲解,希望对读者的学习有所帮助。