Node.js 蚕食计划(二)—— 使用 http 模块搭建 Web 服务器
Node.js 开发的目的就是为了用 JavaScript 编写 Web 服务器程序
这次就来介绍用 http 模块搭建服务器
一、项目构建
每个 Node 程序都可以看作一个模块,而每个模块都应该有一个 package.json 文件
package.json 用来定义模块的属性,还可以用来指明程序的依赖项。它可以手动创建,也可以通过 init 命令自动创建
npm init
如果还需要引入第三方模块,可以在 package.json 中手动配置,也可以通过 install 命令安装
比如安装一个第三方模块 mime
npm install mime -S
安装之后,会在项目根目录下创建一个 node_modules 文件夹,用来存放第三方依赖项
然后手动在根目录下创建一个 lib 文件夹和 public,分别用来存放服务端代码和客户端代码
二、创建基本服务
用 Node.js 创建服务非常简单,调用 http 模块的 createServer 方法就可以了
http 模块的完整 API 可以参考官方文档
在 lib 目录下创建 server.js
/* 如果使用 vscode 启服务,记得修改 launch.json 的入口文件路径 */
// 创建 http 服务
let http = require('http');
let server = http.createServer(function(req, res){
res.end('Wise Wrong');
});
// 绑定监听端口
server.listen(4567);
// 创建完成后打开对应网页
let c = require('child_process');
c.exec('start http://localhost:4567');
响应数据的时候,必须有一个 res.end() 方法来结束响应
上面的 res.end('Wise Wrong') 是简写,对于大型的响应需要这么写:
res.write('Wise Wrong');
res.end();
然后调用了 child_process 模块,这也是 Node.js 自带的模块
这个模块可以用来创建子进程,它的 exec 命令可以执行 shell 命令
三、响应静态文件
将 html 文件放在 public 目录下
首先需要获取到文件的路径,可以手动拼一个
let filePath = false;
if(req.url == '/') {
filePath = 'public/index.html';
} else {
filePath = 'public' + req.url;
}
let absPath = './' + filePath;
如果引入了 path 和 url 模块,就更方便的获取到文件路径
path.resolve() 将路径或者路径片段解析为绝对路径
url.parse() 将字符串解析为一个 url 对象,可以用来处理 GET 请求
读取静态文件需要用到 Node 自带的 fs (文件系统)模块
// server.js
let http = require('http');
let fs = require('fs');
let url = require('url');
let path = require('path');
let c = require('child_process');
let server = http.createServer(function(req, res) {
// 获取根目录
let root = path.resolve('.');
// 获取请求路径
let filePath = url.parse(req.url).pathname;
if (filePath === '/') filePath = '/index.html';
// 生成文件路径
let absPath = path.join(root, '/public', filePath);
// 从硬盘读取文件
fs.readFile(absPath, function (err, data) {
if (err) {
// 重写响应头,返回 404
res.writeHead(404, {'Content-Type': 'text/plain'});
res.write("not found");
} else{
// 重写响应头,返回 200
res.writeHead(200, {'Content-Type': 'text/html'});
// 响应文件内容
res.write(data);
}
// 结束响应,发送数据
res.end();
});
});
server.listen(4567, function() {
console.log('Server listening on port 4567');
// 服务启动成功后打开对应链接
c.exec('start http://localhost:4567');
});
Node 本身不会往客户端写任何响应,所以需要我们自己写响应事件,修改响应头
在 node 环境中打开 server.js 就会加载 index.html
四、处理 POST 请求
在 public 目录下再创建一个 form.html,用来提交 post 请求
服务端可以通过 req.method 来判断请求类型是 GET 还是 POST 或者其他
POST 请求会触发 data 事件,监听 data 事件就能获取数据
let postData = '';
// 监听 data 事件
req.on("data", function (chunk) {
// 拼接 post 过来的数据
postData += chunk;
});
这时获取到的还只是一个字符串,通常需要将它转为对象,这时就需要引入 querystring 模块
let queryData = querystring.parse(postData);
然后可以通过 fs.writeFile 将数据保存到文件里面
fs.writeFile(path.join(__dirname, 'wise.txt'), postData, function(){});
这里的 __dirname 是一个神奇的变量,它的值是该文件所在目录的路径
同一个程序中 __dirname 可以有不同的值
在 server.js 中它指向的就是 lib 目录的路径
继续完善 server.js
// server.js
let http = require('http');
let fs = require('fs');
let url = require('url');
let path = require('path');
let qs = require('querystring');
let c = require('child_process');
let server = http.createServer(function (req, res) {
// 获取根目录
let root = path.resolve('.');
// 获取请求路径
let filePath = url.parse(req.url).pathname;
if (filePath === '/') filePath = '/index.html';
// 生成文件路径
let absPath = path.join(root, '/public', filePath);
switch (req.method) {
case 'GET':
// 从硬盘读取文件
fs.readFile(absPath, function (err, data) {
if (err) {
// 重写响应头,返回 404
res.writeHead(404, { 'Content-Type': 'text/plain' });
// 响应文件内容
res.write("not found");
} else {
// 重写响应头,返回 200
res.writeHead(200, { 'Content-Type': 'text/html' });
// 响应文件内容
res.write(data);
}
// 结束响应,发送数据
res.end();
});
break;
case 'POST':
let postData = '';
// 监听 data 事件
req.on("data", function (chunk) {
// 拼接 post 过来的数据
postData += chunk;
});
// 监听 end 事件
req.on("end", function () {
// 使用 querystring 模块将字符串数据转为对象
let queryData = qs.parse(postData);
// 将数据以 json 字符串的形式保存到文件中
fs.writeFile(path.join(__dirname, 'wise.txt'), JSON.stringify(queryData), function (err) {
if (err) throw err;
console.log("Export Account Success!");
});
// 重写响应头
res.writeHead(200, { "Content-Type": "text/plain" });
res.end("success");
});
break;
}
});
server.listen(4567, function () {
console.log('Server listening on port 4567');
// 服务启动成功后打开对应链接
c.exec('start http://localhost:4567/');
});
最终效果