# 认识 Node.js
Node.js 是一门服务器编程语言,它也遵循了 ECMAScript 语法规范,在此规范的基础上加入了 Node.js API,包含处理 http 请求、处理文件、socket 编程等。Node.js API 和 ECMAScript 两者结合组成了 Node.js,完成 Server 端的任何操作,为客户端浏览器进行服务。
我们快一起去探索 Node.js 的世界吧~
# 知识点
- Node.js 的介绍
- 下载和安装 Node.js
# Node.js 的介绍
在实验介绍中说了,Node.js 是一门服务器编程语言。它发布于 2009 年 5 月,由 Ryan Dahl 开发,也是一个基于 Chrome V8 引擎的 JavaScript 运行环境,使用了一个事件驱动、非阻塞式 I/O 模型,让 JavaScript 运行在服务端的开发平台,它让 JavaScript 成为与 PHP、Python、Perl、Ruby 等服务端语言平起平坐的脚本语言。
Node.js 有如下特点和优势:
- 它是一个 JavaScript 运行环境
- 依赖于 Chrome V8 引擎进行代码解析
- 事件驱动(event-driven)
- 非阻塞 I/O (non-blocking I/O)
- 轻量、可伸缩,适于实时数据交互应用
- 单进程,单线程
学到这里有的同学可能有些疑惑,这么多特点都是什么意思呢?Node.js 相对于其他服务器语言,例如 Ruby、Java 又有什么区别和优势呢?别着急,老师马上为大家解答。
传统的服务器开发语言(例如 java)是多线程的,例如我们在淘宝商城购物,如果只有一个顾客发送请求,那显然是没问题的。但是当有成百上千万的用户同时访问的时候,我们肯定不希望别人买完以后才能轮到自己购买,这显然要等待很长时间。但是如果 CPU 只有一个核心的时候或者 CPU 只有一个线程的时候,确实要等待别人购买完毕以后才能购买。这就像在饭馆吃饭,如果只有一个厨师的话,同一时间只能给一个顾客做菜,这样必须要等待这一个厨师为别人做完菜以后才能为自己做菜。
为了解决这个问题,我们可以多增加几个厨师来同时为这些顾客做菜,这就是 java 语言的处理方式:使用多线程并发执行,但是这种方式势必会增加聘请厨师的成本和消耗更大的厨房空间,这无疑是一个资源的浪费,而且这种多线程模型,CPU(厨师) 在为客户服务的时候不能做其他的事,例如使用 I/O 读取文件的时候,只能等待文件系统读取文件完毕以后,才能继续做其他事。
为了解决上述问题 Node.js 应运而生。Node.js 是单线程模型、非阻塞 I/O、采用事件循环机制的原理进行处理。如下图:
非阻塞 I/O 的意思就是,文件系统在进行 I/O 操作的时候,Node.js 这个线程还可以做其他的事,当文件系统读取文件完毕时,通过事件的回调函数告诉 Node.js 线程,然后 Node.js 把读取的内容响应给用户。
Node.js 中的 I/O 操作可以理解成生活中的一个幕后工作者,就像在餐馆点餐这个场景中,饭馆可以直接去把点餐的任务派发给外部擅长做菜、煮饭、酿酒的厂家,这些厂家都是非常擅长这些工作的,Node 中也有各种擅长做某件事的模块。这样当这些幕后的厂家完成这些做菜、饭、酒的事件以后,把做完的饭菜酒等食品交给厨师,然后由厨师统一的让服务员把饭菜交给顾客。这样就大大提高了整个餐馆的运营速度。
如果大家还不明白的话,我们再看下一个例子,Node.js 就像国王,国王每天都在写任务清单,然后派发给大臣。大臣把任务清单交给下面的官员去做,而这个时候国王还是可以继续写任务清单的。当官员完成任务这个事件后,统一的任务结果交给国王(以事件回调函数的形式通知 Node.js)。此事件反复进行,这便是事件循环。所以说除了国王(Node.js)线程以外,每件事都是并行发生的,这便是 Node.js 单线程和事件循环能同时处理多个请求的原理。
# 在 Window 和 Mac 中下载和安装 Node.js
# 下载 Node.js
点击 Node 官网 进入 Node
官方网站,效果如下:
网站自动识别系统为 Windows
,默认给出了 Windows
的 Node
下载地址,如果使用 Mac
电脑访问则自动识别系统为 Mac
系统,自动给出 Mac 版下载地址。这里给出了两个版本的下载地址:
一.14.17.3 LTS 版(左侧)
即 Long Time Support 版(长时间支持版),即稳定版。
二.16.4.2 Current (右侧)
即最新版,包含最新的特性,但不稳定。
这里推荐下载 LTS 长时间支持版,点击 14.17.3 LTS 进行下载,下载后的文件如下:
另: Mac
下载相应 Mac
版本即可。
# 安装
windows
和 Mac
下都是直接双击安装文件,一路下一步即可。
# 查看是否安装成功
安装完成后,使用 cmd
进入到控制台,输入如下命令测试 Node
是否安装成功。
node - v; // 查看 node 版本 | |
npm - v; // 查看 npm 版本 |
如果看到如下版本信息,则表示 Node
安装成功。
需要说明的是:本课程是基于 node v14+ 的版本,低于该版本,请自行更新至对应版本中。
安装 Node
的时候会同时帮我们安装 npm (Node Package Manager)
即 Node
包管理工具,用于下载依赖的 node
包。
# linux 版 Node 下载和安装
打开我们的实验环境,在右侧控制台中输入如下命令:
node -v |
效果如下:
发现我们并没有安装 Node
就已经出现了 Node
版本,原因是我们的实验环境中已经默认安装了 Node 14.15.1
版。
下面给大家讲解一下 linux
下 Node.js
的下载和安装。
# 下载
进入 Node 官网 点击下图中的 Downloads
。
在弹出的界面中显示的 Node 的全部下载版本,这里我们下载 Linux Binaries(x64
版。见下图:
同学们也可以使用下列命令直接下载我们云服务器上的 Linux Node
安装文件。
wget https://labfile.oss.aliyuncs.com/courses/4380/node-v14.17.3-linux-x64.tar.xz |
使用上述命令下载后效果如下:
然后使用如下命令进行解压缩:
tar -xvf node-v14.17.3-linux-x64.tar.xz |
解压后效果如下
解压完毕后就可以把 node-v14.17.3-linux-x64.tar.xz
文件删除了。
然后使用如下命令对解压后的 node 文件夹进行改名:
mv node-v14.17.3-linux-x64 node |
改名后效果如下:
这时再次输入 node -v
查看版本,发现版本仍然是 14.15.1。
说明我们的 Node
安装并没有完成,如果想使用最新安装的 14.17.3 版,需要使用以下命令。
cd node/bin | |
./node -v |
注意这里是使用./node 来使用当前路径下的 node
命令,效果如下:
发现版本为 14.17.3 ,说明我们已经使用了自己下载的最新版本的 Node,但是每次都需要进入到 Node
安装路径的 bin
路径下才能使用 Node
命令,比较麻烦。如何能让我们在系统的任何地方都能使用最新安装的 Node
呢?这里需要修改我们的环境变量 PATH
, 把 node/bin
这个路径添加到 PATH
下。
# 修改环境变量 PATH
为了在全局使用最新安装的 Node
,我们还需要修改一个配置文件 ~/.zshrc
。
PATH
作用:当我们在控制台输入命令的时候(例如 node -v),系统是去 PATH
环境变量下配置的路径中寻找这个命令是否存在,查找的路径顺序为从左到右依次查找(linux 路径分隔符为 :)。如果发现对应的 PATH
路径下有 node
命令就会使用,否则就会报错。
首先使用下列命令查看我们的环境变量:
env | grep node |
效果如下:
发现 PATH
中有 /usr/sbin/nodejs/bin
,这个路径就是我们实验环境默认安装的 14.15.1
的 Node 安装路径,如果想让我们新安装的 Node 优先执行的话,只需要把新版本 Node 的安装路径下的 bin 路径放到 /usr/sbin/nodejs/bin
路径前即可。
使用下列命令修改 ~/.zshrc
。
vim ~/.zshrc |
使用 i
进入插入模式,在文件最后加入如下内容:
export PATH=/home/project/node/bin:$PATH |
然后输入 ESC 回到普通模式,输入 :
进入命令模式,然后 输入 wq
进行保存。修改完成的文件并不会马上生效,需要使用如下命令让刚才配置的环境变量生效。
source ~/.zshrc |
最后输入 node -v
重新查看 node
版本,效果如下:
发现现在全局使用的已经是我们最新安装的 node
了。
# Web 请求
HTTP 协议(Hyper Text Transfer Protocol)是超文本传输协议,它是基于请求 - 响应的一个协议,即客户端浏览器给服务器发送一个 HTTP 请求,然后服务器对客户端的请求作出响应。我们开发的基于浏览器的 B/S 架构的程序都是基于 HTTP 协议(也有 HTTPS)的,例如登录、注册、查询商品等。
当用户在 URL 中输入一个网址到看到页面,大体经过如下几步:
- DNS 解析,建立 TCP 连接,发送 HTTP 请求。
- Server 端接收 HTTP 请求,处理,返回结果。
- 客户端接收到返回数据,进行页面渲染显示内容。
本节实验要给同学介绍的就是发送请求最常用的两种方式,GET 和 POST。
我们快一起去学习吧!
# 知识点
- GET 请求
- POST 请求
# 处理 GET 请求
通过浏览器向服务器发送请求的常用方式分为两种,一种是 GET
方式,另一种是 POST
方式,前者请求的安全性不高,常用于查询的操作,如查询用户信息、查询博客信息等。
GET 请求传递参数是在 URL 后面加入一个 "?" ,然后在 "?" 后面加入想要传递的参数,多个参数之间用 "&" 隔开。格式如下:
http://localhost:8000/index?id=1001&name=abc |
首先在右侧控制台中输入以下命令来初始化 npm 环境,初始化完成后会生成一个 package.json 文件。
npm init -y |
在 project
文件夹下,新建 index.js
文件,并加入下列代码:
const http = require("http"); | |
const server = http.createServer((req, res) => { | |
res.writeHead(200, { "Content-Type": "text/html;charset=utf-8" }); | |
// 通过请求对象获取完整的请求地址并保存在变量 url 中 | |
let url = req.headers["x-scheme"] + "://" + req.headers.host + req.url; | |
// 将变量传入实例化方法中,并实例化一个 URL 对象 | |
const myURL = new URL(url); | |
let params = myURL.searchParams.toString(); | |
res.write(params); | |
res.end(); | |
}); | |
// 服务侦听 8080 端口 | |
server.listen(8080, () => { | |
console.log("服务器运行在 8080 端口..."); | |
}); |
在本示例中代码解析如下:
定义一个变量
url
用于保存req
对象请求的完整地址,其中req.headers['x-scheme']
返回请求协议名称,如http
或https
,而req.headers.host
返回请求的域名和端口,req.url
返回请求的详细路径,包含查询字符串。获取并保存完整的请求地址后,实例化一个
URL
对象,在实例化对象的方法中,可以传递两个参数,第一个参数是必选项,表示要解析的绝对或相对的网址,第二个参数是可选项,表示要解析的基础网址,如果第一个参数是绝对地址,则第二个可以省略,如本示例中的代码;如果第一个对数是相对地址,则第二个参数必须添加请求地址的协议名称、域名和端口。如果将示例中绝对的请求地址修改成相对地址,那么在实例化
URL
对象时,代码修改如下所示:// 通过请求对象获取相对请求路径并保存在变量 url 中
let url = req.url;
// 在实例化过程中,通过第二个参数添加请求协议的名称、域名和端口
const myURL = new URL(
url,
req.headers["x-scheme"] + "://" + req.headers.host
);
这两种代码实现的方式在使用时,结果都是一样的,但在实际运用时,传入完整的请求地址,使用会更多些。
在控制台中输入以下命令运行该程序。
node index.js |
然后点击右侧的 “Web 服务”,并在 simplelab.cn
地址后加入 ?id=1001&name=abc
,刷新页面,效果如下:
页面中显示的内容就是以字符串形式输出的 GET 请求参数,如果需要使用,也可以通过指定参数名称获取对应的值。
它这里不是实时刷新的,当修改了服务器端的代码,就需要重新运行代码。
res.write()
传入的是字符串,没有自动换行。
# 处理 POST 请求
相对于 GET
方式请求, POST
请求在传输数据时,要安全很多,因此,常用用于数据的添加、删除、修改操作,例如新建博客、删除博客、修改博客等就会使用到 POST
请求。
在 URL
中输入地址直接访问属于 GET
请求,而 POST
请求则常用于表单数据的提交,先在 form
中设置 method=post
,当用户点击提交表单按钮时就会发送一个 POST
请求,并把表单中用户填入的数据传递给服务器。但是手工编写表单的代码显然比较麻烦,有没有一种更加简单的方式发送 POST
请求呢?
答案是有的。可以使用 Postman
这个工具来帮助我们发送 POST
请求。
# Postman 下载安装
首先在右侧桌面环境打开 Firefox 浏览器,然后在 URL 地址栏中输入 http://postman.com/downloads
进入到下载页面。系统自动识别下载 linux 版本的 postman
。
然后单击 Download the App
按钮进行下载,在弹出的界面中选择 Linux 64-bit 下载(如下图)。
单击” 保存文件” 按钮进行保存,如下图。
下载完毕后右上角会提示 “已完成”。
然后点击 “显示全部下载” 会弹出以下界面。
点击 “打开所在文件夹”,然后右键点击 Postman-linux-x86_64-8.11.1.tar.gz
文件重命名为 postman.tar.gz
,然后使用鼠标把该文件拖拽到左侧 shiyanlou
目录下。然后在实验楼路径中点击右键,选择 “在此打开终端”(如下图)。
然后在终端中输入如下命令进行解压。
sudo tar -zxvf postman.tar.gz |
使用如下命令启动 postman
./Postman/Postman |
启动后点击左上角的 "File"---"new" 新建一个窗口,效果如下:
然后申请一个 Postman
账号并点击 Sign in
登录(此过程不再截图,同学们自行完成)。登录成功后如下图,然后点击 Create new
。
在弹出的界面中点击 HTTP Request
,如下图:
如果想发送 HTTP
请求还需要装一个 Postman
客户端才能正常发送请求。使用下列命令下载 Postman
客户端。
cd /home/shiyanlou | |
wget https://labfile.oss.aliyuncs.com/courses/4380/postmanAgent.tar.gz |
注意:如果想复制如上命令到云课桌面环境进行粘贴的话,可以借助右侧 “剪切板” 功能。先复制好想要粘贴的命令,然后点击右侧 “剪切板”,把复制的内容粘贴到剪切板中,然后点击保存即可(如下图),然后就可以在云课桌面环境中进行粘贴了。
下载完毕后截图如下:
然后使用如下命令进行解压安装。
cd /home/shiyanlou | |
sudo tar -zxvf postmanAgent.tar.gz |
# 编写代码处理 POST 请求
然后在 /home/shiyanlou/
路径下新建 index.js
文件,然后编写代码如下:
const http = require("http"); | |
const server = http.createServer((req, res) => { | |
if (req.method === "POST") { | |
console.log("content-type:", req.headers["content-type"]); // 获取请求类型 application/json | |
// 读 post 数据 | |
let postData = ""; //postData 用来存储传递给服务器的全部数据 | |
// 分段循环传输数据,每次传递数据都会执行后面的回调函数 | |
req.on("data", (chunk) => { | |
postData += chunk.toString(); //chunk 是二进制数据 所以要把它转换成字符串 | |
}); | |
// 当数据传输完毕会执行 end 事件后的回调函数 | |
req.on("end", () => { | |
console.log("postData:", postData); | |
res.end("Hello"); // 在这里返回因为是异步 | |
}); | |
console.log("test"); // 这里先被打印 因为上面的代码是异步的 | |
} | |
}); | |
server.listen(8080, () => { | |
console.log("服务器运行在 8080 端口..."); | |
}); |
在终端中输入以下命令运行该程序。
cd /home/shiyanlou | |
node index.js |
最终效果如下图所示:
res.end()
可以传入字符串作为参数,也可以不传入,所谓结束返回的标志。
# 使用 Postman 发送 POST 请求
然后打开刚才的 Postman
界面,根据下图依次选择 POST
请求,输入请求地址 http://localhost:8080
。然后点击 Body
,选择 raw<span> </span>
和 JSON
,输入发送的内容后点击 Send
按钮。
当看到上图中的返回内容 Hello
时,说明程序已经调试成功了,控制台输出如下:
# 实验总结
本节实验给大家介绍了两种最常用的 Web 请求,分别是 GET 和 POST。这里我们来总结一下:前者的链接中包含请求参数,安全系数低于 POST 请求。但 GET 请求也有自己的用处,比如查询操作时,显然 GET 请求是不错的选择。
# 初始化路由
# 知识点
- 认识路由
- 配置路由
- 访问路由
- 路由开发
# 认识路由
路由,顾名思义,它是指路径的指向或由来,即访问一个地址后它的指向,确定地址指向后,将会根据不同的路由地址,编写相应的代码,如下图所示。
一次地址的指向,实质上是一次数据请求的过程,在这种请求的过程中,还可以携带请求的方式,如 POST 或 GET,同时,也可以携带请求的参数,根据这些请求携带的方式和参数,即使是同一个地址,也可以执行不同的代码,如下图所示。
借助路由的这些特性,被广泛地应用于项目中各页面的切换,数据接口的请求,因此,路由是项目开发中必须要掌握的内容。在理解了它的重要性之后,如何去配置一个路由呢?带着这个问题,下面来说路由的配置方法。
# 配置路由
在 node
中配置路由十分简单,首先,使用以下命令下载搭建好的开发环境项目包,地址如下所示。
wget https://labfile.oss.aliyuncs.com/courses/4380/router.zip && unzip router.zip |
然后,解压该项目包至 project
文件夹中,并打开项目包 router
文件夹,找到 bin
文件夹,在该文件夹下,创建一个名称为 reqRouters
的 js 文件,加入如下所示的代码:
const reqRouters = (req, res) => { | |
if (req.path === "/aa") { | |
return "首页"; | |
} | |
if (req.path === "/bb") { | |
return "列表页"; | |
} | |
if (req.path === "/cc") { | |
return "详细页"; | |
} | |
}; | |
module.exports = reqRouters; |
在上述代码中,定义了一个名称为 reqRouters
函数,函数中,参数 req
表示请求时携带的对象,用于创建服务请求时的回调函数使用,在这个 req
对象中,通过 path
可以获取到请求时的路由地址,根据获取的不同地址,返回不同的内容,最后,输出这个名称为 reqRouters
的函数。
接下来,再次找到 bin
文件夹,打开名称为 app
的 js 文件,删除原有的内容,加入如下所示的代码。
const reqRouters = require("./reqRouters"); | |
const serverHandle = (req, res) => { | |
res.setHeader("Content-Type", "application/json"); | |
req.path = req.url.split("?")[0]; | |
const result = reqRouters(req, res); | |
if (result) { | |
res.write(result); | |
res.end(); | |
return; | |
} | |
res.writeHead(404, { "Content-Type": "text/plain" }); | |
res.write("404 Not Found\n"); | |
res.end(); | |
}; | |
module.exports = serverHandle; |
在上述代码中,首先,使用 require
输入 reqRouters
模块,用于获取请求输出的内容,其次,定义一个名称为 serverHandle
的函数,用于服务创建时的回调,在该函数中,向 reqRouters
函数传入请求的路由地址,获取输出的内容并保存至变量 result
中,最后,判断变量 result
中是否有内容,如果有,则直接输出在页面中,否则,在页面中输出 "404 Not Found" 的信息。
最后,找到 bin
文件夹,打开名称为 index
的 js 文件,删除原有的内容,加入如下所示的代码。
const http = require("http"); | |
const serverHandle = require("./app"); | |
const port = 8080; | |
const server = http.createServer(serverHandle); | |
server.listen(port, () => { | |
console.log("服务器运行在 8080 端口..."); | |
}); |
在上述代码中,首先,分别导入 http
和 serverHandle
模块,前者用于创建一个新的服务,是 node
自带模块, 后者用于创建服务后的回调函数,当服务创建成功后,使用 listen
方法,在指定的 8080 端口下侦听,当服务启动后,就可以在浏览器的地址栏中,根据启动的地址和端口访问这个服务了,由于配置的路由在服务中,这时,就可以按配置的路由访问页面了 🤪
# 访问路由
要访问路由,需要先启动服务,因此,首先,在项目文件夹 router
下,打开终端界面,并输入如下指令。
npm run dev |
服务启动后的界面效果如下图所示:
当服务成功启动后,就可以在浏览器中,根据指定的地址和端口,访问相应的页面内容,效果如下所示:
当在浏览器地址栏中输入 localhost:8080/aa
时,它的 url.path
值为 '/a' ,传给 reqRouters
函数后,则返回 "首页" 字符,因此,页面中输出 "首页" 内容,其他输入地址依此类推,根据不同的 url.path
值,向页面输出不同的内容,最终实现路由的功能。
# 路由开发
# 获取请求方式
在发送一次请求时,不同的请求方式,将会返回不同的数据,目前常用的请求方式分为 GET
和 POST
两种,前者常用于查询请求,后者用于增加、修改和删除的请求,那么,在 node
中,如何获取路由中的请求方式呢?下面通过一个示例来进行说明。
首先,使用以下命令获取搭建好的路由初始化项目包,地址如下所示。
wget https://labfile.oss.aliyuncs.com/courses/4380/router-param.zip && unzip router-param.zip |
打开项目包 router-param
中的 bin
文件夹,在该文件夹下,找到并打开名称为 reqRouters
的 js 文件,删除原有的代码,加入如下所示的代码:
const reqRouters = (req, res) => { | |
if (req.method === "GET") return "这是一次 GET 方式请求"; | |
if (req.method === "POST") return "这是一次 POST 方式请求"; | |
}; | |
module.exports = reqRouters; |
在上述代码中,首先,通过 req
对象中的 method
属性获取到服务请求的方式,然后根据不同的请求方式,返回不同的文字内容,最后,输出这个名称为 reqRouters
的函数。
修改完成后,在项目文件夹 router-param
下,打开终端界面,并输入如下指令。
npm run dev |
服务启动后的界面效果如下图所示:
当服务成功启动后,就可以在浏览器中,根据指定的地址和端口,访问相应的页面内容,由于是以 GET
和 POST
发送请求,因此,需要借助之前安装好的 Postman
工具,更多与 Postman
相关的内容,请点击 👉参阅资料, 其实现的效果分别如下:
- 如果是
GET
方式请求,则返回 "这是一次 GET 方式请求" 内容,如下图所示。
- 如果是
POST
方式请求,则返回 "这是一次 POST 方式请求" 内容,如下图所示。
在上述效果示意图中,我们看到,通过 req
对象中的 method
属性可获取每次请求的方式,除获取每次请求的方式之外,还可以获取路由中传递的参数值,根据请求方式的不同,获取的方式也不一样,接下来,我们先来介绍获取 GET
方式传入的参数值。
# 获取 GET 方式传参
在进行 GET
方式请求时,如果需要传递参数值,通常添加在 url
的后面,使用 ?
进行标记,通过 key=value
的形式进行设置,多个参数值之间使用 &
进行连接,如下图所示:
获取 GET
方式传参的过程实质是根据参数名称,获取对应值的过程,在这个过程中,先将传入的地址作为参数,实例化一个 URL
对象,然后通过该对象获取表示网址查询参数的 searchParams
对象,最后,在 searchParams
对象中,根据传入参数名称,获取对应的值,接下面我们通过一个示例来演示参数值获取的过程。
打开项目包 router-param
中的 bin
文件夹,在该文件夹下,找到并打开名称为 reqRouters
的 js 文件,删除原有的代码,加入如下所示的代码:
// 引入 url 模块 | |
var url = require("url"); | |
const reqRouters = (req, res) => { | |
if (req.method === "GET") { | |
const myURL = new URL(req.url, req.headers.host); | |
let params = myURL.searchParams; | |
if (params.get("id") && params.get("name")) { | |
return params.get("id") + "," + params.get("name"); | |
} else { | |
return "没有传入相应参数!"; | |
} | |
} | |
}; | |
module.exports = reqRouters; |
在上述代码中,首先判断请求否是为 GET
方式 ,如果是,则实例化一个 URL
对象,在实例化过程中,需要传入两个参数,第一个参数是请求的 url
地址,通过 req.url
获取,第二个参数是 base
表示要解析的基本网址,包括地址的域名和端口,如果 url
地址是相对的,则需要添加第二个参数,如果是绝对的,则可以省略第二个参数。
在上述代码中,通过这个实例化后的 URL
对象,将保存网址查询参数的 searchParams
对象赋值给变量 params
,然后再判断 params
中是否存在对应参数名称的值,如果存在,则返回该对应值,否则,返回 "没有传入相应参数!" 的错误信息。
修改完成后,在项目文件夹 router-param
下,打开终端界面,并输入如下指令。
npm run dev |
当服务成功启动后,借助之前安装好的 Postman
工具,使用 GET
方式,在当前域名下,首先请求一个指定参数名称和值的地址,然后请求一个不传参数的地址,在点击 "发送" 按钮后,其实现的效果分别如下图所示:
在上述效果示意图中,我们看到,路由中传入的两项参数都获取成功,相比获取 GET
方式传入的参数值而言,获取 POST
方式传入的参数值就要复杂很多,接下来我们再来说下获取 POST
方式传入参数值。
# 获取 POST 方式传参
相对于使用 GET
方式请求而言,使用 POST
方式更加安全,因此,常用于增加、修改和删除数据的操作,使用 POST
方式传参时,携带的请求数据并不在路由中,而是在请求对象中,因此,需要绑定请求过程的两个事件,一个是 data
事件,在该事件中获取并累加每次请求传入的参数值, 另一个是 end
事件,该事件在请求结束时触发,在事件中处理累加后的请求参数,并输出至页面中,接下面我们通过一个示例来演示参数值获取的过程。
打开项目包 router-param
中的 bin
文件夹,在该文件夹下,找到并打开名称为 index
的 js 文件,删除原有的代码,加入如下所示的代码:
const http = require("http"); | |
const serverHandle = (req, res) => { | |
if (req.method === "POST") { | |
var strPOST = ""; | |
// 绑定数据请求事件,每当接受到请求体的数据,就累加到 strPOST 变量中 | |
req.on("data", function (chunk) { | |
strPOST += chunk; | |
}); | |
// 绑定数据请求结束事件,向页面输出指定的参数值 | |
req.on("end", function () { | |
res.setHeader("Content-Type", "application/json"); | |
let objPOST = new URLSearchParams(strPOST); | |
if (objPOST.get("id") && objPOST.get("name")) { | |
const result = objPOST.get("id") + "," + objPOST.get("name"); | |
res.write(result); | |
} else { | |
res.write("没有传入相应参数!"); | |
} | |
res.end(); | |
return; | |
}); | |
} | |
}; | |
const port = 8080; | |
const server = http.createServer(serverHandle); | |
server.listen(port, () => { | |
console.log("服务器运行在 8080 端口..."); | |
}); |
在上述代码中,首先判断请求否是为 POST
方式 ,如果是,则分别绑定请求时的两个事件,一个是 data
事件,在该事件中,将每次发送参数值累加并保存在变量 strPOST
中, 另一个是 end
事件,在该事件中 ,当请求结束后,将传入并累加后的参数解析为查询字符串,并使用它来实例化新的 URLSearchParams
对象,最后,在 URLSearchParams
对象中,先判断传入的参数名称是否存在获取对应的值,如果存在,则并输出到页面中,否则,向页面输出 "没有传入相应参数!" 的错误信息。
修改完成后,在项目文件夹 router-param
下,打开终端界面,并输入如下指令。
npm run dev |
当服务成功启动后,借助之前安装好的 Postman
工具,使用 POST
方式,请求一个当前域名下指定参数名称和值的地址,点击 "发送" 按钮后,其实现的效果如下图所示:
# Node.js 文件操作
我们知道,在前端在开发时,JavaScript 是一种依赖浏览器解析的语言,在访问文件时,需要依赖浏览器的兼容性,操作文件的能力很弱 ,而 Node
则这方面有很强的能力,那么,它是如何操作一个文件的呢?带着这个问题,我们快速地进入到今天的学习中来吧!
# 知识点
- 文件读取操作
- 文件写入操作
- 删除文件操作
# 文件读取操作
在 node
中,如果要读取一个文件,就必须引入一个名称为 fs
的模块,而 fs
是 file-system
的简写,表示文件系统,在该模块中提供了全部操作文件的方法,因此,如果想去读取一个文件,必须先导入该模块,代码如下所示。
const fs = require("fs"); |
完成模块导入之后,就可以使用该模块中读取文件的方法 fs.readFile()
,该方法是一个异步读取文件的方法,它的调用格式如下所示:
fs.readFile(path[, options], callback) |
在上述格式中,第一个参数 path
表示读取文件的路径,通常是一个相对路径,如 "./public/msg.txt",第二个参数 options
是一个可选项,在该项中可以设置读取文件时的编码格式 encoding
,文件打开的行为 flag
,允许中止正在进行的读取文件 signal
,其中 flag
包含多种方式,具体如下表所示:
(诶?这都已经是 readFile 了,那么为什么要写这些 flag 呢?不就是 r 了吗)
flag 名称 | 描述 |
---|---|
a | 打开文件进行追加。 如果文件不存在,则创建该文件。 |
ax | 类似于 a 但如果路径存在则失败。 |
a+ | 打开文件进行读取和追加。 如果文件不存在,则创建该文件。 |
ax+ | 类似于 a+ 但如果路径存在则失败。 |
as | 以同步模式打开文件进行追加。 如果文件不存在,则创建该文件。 |
as+ | 以同步模式打开文件进行读取和追加。 如果文件不存在,则创建该文件。 |
r | 打开文件进行读取。 如果文件不存在,则会发生异常。 |
r+ | 打开文件进行读写。 如果文件不存在,则会发生异常。 |
rs+ | 以同步模式打开文件进行读写。 指示操作系统绕过本地文件系统缓存。 |
w | 打开文件进行写入。 如果它不存在则创建,如果它存在则截断该文件。 |
wx | 类似于 w 但如果路径存在则失败。 |
w+ | 打开文件进行读写。 如果它不存在则创建,如果它存在则截断该文件。 |
wx+ | 类似于 w+ 但如果路径存在则失败。 |
第三个参数 callback
是一个回调函数, 在回调函数中,传入了两个参数,一个参数的名称为 error
,表示文件未读取成功时的错误信息,另一个参数的名称为 data
,表示文件读取成功时的内容信息,接下来通过一个示例来演示文件读取的过程。
找到并打开 project
文件夹,在该文件夹下,创建一个名称为 public
的文件夹,在 public
文件夹中,新建一个名称为 msg
的记事本文件,打开该文件,添加下列文字内容并保存。
蓝桥,一个学习知识的好地方! | |
--程序员寄语-- |
再次找到并打开 project
文件夹,在该文件夹下,新建一个名称为 readfile
的 js 文件,打开该文件,并添加下列代码:
// 导入 fs 模块 | |
const fs = require("fs"); | |
// 调用模块中异步读取文件的方法 | |
fs.readFile("./public/msg.txt", "utf-8", (error, data) => { | |
if (error) throw error; | |
console.log(data); | |
}); |
在上述代码中,先导入 fs
模块,再调用模块中的异步读取文件的方法 readFile
,以 "utf-8" 的编码格式,读取记事本 msg
文件中的内容,在方法的回调函数中,如果出现错误,则返回相关的错误信息,如果没有出现错误,在控制台输出读取到的文件内容。最后,在 project
文件夹下,打开终端,并使用 node
执行名称为 readfile
的 js 文件,最终的效果如下图所示:
从最终读取出的内容效果看,使用 fs
模块中的 readFile
方法,可以很方便地读取到文件中的内容,
# 文件写入操作
在 fs
模块中,与文件读取方法 readFile
相对应, writeFile
方法则是用于文件的写入,该方法也是一个异步写入文件的方法,它的调用格式如下所示:
fs.writeFile(file, data[, options], callback) |
在上述格式中,方法中第一个参数 file
表示要写入的文件名,第二个参数 data
表示需要写入的内容,第三个参数 options
是一个可选项,它与文件读取方法中的功能一样,在此不再赘述,第四个参数 callback
是一个回调函数, 在回调函数中,传入了一个名称为 error
的参数,表示文件没有写入成功时的错误信息,接下来通过一个示例来演示文件写入的过程。
找到并打开 project
文件夹,在该文件夹下,新建一个名称为 writefile
的 js 文件,打开该文件,并添加下列代码:
// 导入 fs 模块 | |
const fs = require("fs"); | |
// 调用模块中异步写入文件的方法 | |
fs.writeFile("./public/msg.txt", "今天是一个好日子", (error) => { | |
if (error) throw error; | |
console.log("写入成功!"); | |
}); |
在上述代码中,先导入 fs
模块,再调用模块中的异步写入文件的方法 writeFile
,将字符串 "今天是一个好日子" 写入 msg
文件中,由于该文件已存在,因此,写入的内容将会覆盖文件中原有的内容,同时,在方法的回调函数中,如果出现错误,则返回相关的错误信息,如果写入没有出错,则在控制台输出 "写入成功!" 的字样。最后,在 project
文件夹下,打开终端,并使用 node
执行名称为 writefile
的 js 文件,效果如下图所示:
当调用 fs
模块中的 writeFile
方法在指定的文件中写入内容后,文件中的原有内容默认将会被覆盖,只保留最新写入的内容,也可以借助 options
对象中的 flag
属性值,改变写入文件时的方式,由覆盖变为追加内容,接下来通过一个示例来演示它实现的方式。(或者先读取保存,然后字符串拼接再写)
找到并打开 project
文件夹,在该文件夹下,新建一个名称为 writefile2
的 js 文件,打开该文件,并添加下列代码:
// 导入 fs 模块 | |
const fs = require("fs"); | |
// 调用模块中异步写入文件的方法 | |
fs.writeFile( | |
"./public/msg.txt", | |
",明天又是一个大晴天。", | |
{ flag: "a", encoding: "utf-8" }, | |
(error) => { | |
if (error) throw error; | |
console.log("写入成功!"); | |
} | |
); |
打开终端,并使用 node
执行名称为 writefile2
的 js 文件,效果如下图所示:
在上述实现的代码中, flag
值为 "a" 时,表示以追加的方式向文件中写入内容,其他方式大家可以根据这种方式进行变更。
# 删除文件操作
在 fs
模块中,可以使用 writeFile
方法增加一个指定名称的文件,如果不希望使用某个文件,也可以调用 unlink
方法,删除这个文件, unlink
方法也是一个异步的方法,它的调用格式如下所示:
fs.unlink(path, callback); |
在上述格式中,第一个参数 path
表示删除文件的路径,第二个参数 callback
是一个回调函数, 在回调函数中,传入了一个名称为 error
的参数,表示文件没有删除成功时的错误信息,接下来通过一个示例来演示文件删除的过程。
首先找到并打开 project
文件夹,在该文件夹下,再打开 public
文件夹,新建一个文件为 temp
的记事本文件,结构如下图所示:
最后,在 project
文件夹下,新建一个名称为 delefile
的 js 文件,打开该文件,并添加下列代码:
// 导入 fs 模块 | |
const fs = require("fs"); | |
// 调用模块中异步删除文件的方法 | |
fs.unlink("./public/temp.txt", (error) => { | |
if (error) throw error; | |
console.log("删除成功!"); | |
}); |
在上述代码中,先导入 fs
模块,再调用模块中的异步删除文件的方法 unlink
,在方法的回调函数中,如果出现错误,则返回相关的错误信息,如果删除没有出错,则在控制台输出 "删除成功!" 的字样。最后,在 project
文件夹下,打开终端,并使用 node
执行名称为 delefile
的 js 文件,效果如下图所示:
注意:在 fs
模块中的 unlink
方法,只能删除文件,不适用于删除目录,即使是一个空目录,如果要删除目录,需要使用另外一个名称为 rmdir
的方法,关于 rmdir
更的资料,请查看 👉 rmdir 使用方法