(1)Node.js是一个基于Chrome V8 引擎的 JavaScript 运行环境
(2)Node.js官网: http://nodejs.cn/
fs模块是node.js官方提供的、用来操作文件的模块。它提供了一系列的方法和属性,用来满足用户对文件的操作需求
fs.readFile()方法,用来读取指定文件内容
fs.writeFile()方法,用来指定文件中写入内容
导入 const fs = require(‘fs’)
fs.readFile(path[,options],callback)
path:必选参数,字符串,表示文件路径
options:可选参数,表示以什么编码格式来读取文件
callback:必选参数,文件读取完成后,通过回调函数拿到读取的结果
以utf8的编码格式,读取指定文件内容,并打印 err 和 dataStr的值
const fs = require('fs')
fs.readFile('./files/1.txt','utf8',function(err,data) {
console.log(err);
console.log("--------------");
console.log(data);
})
const fs = require('fs')
fs.readFile('./files/1.txt','utf8',function(err,data) {
if(err){
return console.log("文件读取失败:" + err);
}
console.log("文件读取成功,内容为:\n" + data);
})
fs.writeFile(file,data[,options],callbacks)
file:必选参数,需要指定一个文件路径的字符串,表示文件的存放路径
data:必选参数,表示要写入的内容
options:可选参数,表示要以什么格式写入文件内容,默认值utf8
callbacks:必选参数,文件写入完成后的回调函数
const fs = require('fs');
fs.writeFile('./sdsnt.txt',"有一个漂亮小孩,他能洗掉被迫画在脸上的口红,却找不到办法洗掉留在心里的、一样深红的痕迹。如果遇到他,我会对他说:“别怕,你有我呢。” ————贺峻霖",'utf8',function(err) {
console.log(err);
})
const fs = require('fs');
fs.writeFile('./sdsnt.txt',"有一个漂亮小孩,他能洗掉被迫画在脸上的口红,却找不到办法洗掉留在心里的、一样深红的痕迹。如果遇到他,我会对他说:“别怕,你有我呢。” ————贺峻霖",'utf8',function(err) {
if(err) console.log("文件写入失败:\n" + err.message);
console.log("文件写入成功");
})
在使用fs模块操作文件时,,如果提供的操作路径是相对路径时,很容易出现路径动态拼接错误
代码在运行时,会执行node命令时所处的目录,动态拼接出被操作文件的完整路径。
直接使用绝对路径,移植性差,不利于维护
__dirname 双下划线+dirname 表示当前文件所处的目录
fs.readFile(__dirname + 'path','options',function(err,data) {})
fs.writeFile(__dirname + 'path','data','options',function(err,data) {})
使用path模块来处理路径,需要用如下方式导入:
const path = require('path');
使用path.join() 方法,可以把多个路径片段拼接为完整的路径字符串
path.join([...paths]);
…path:(string) ,路径片段的序列
返回值:(string)
注意: '…/'会抵消前面的一级路径
示例
const path = require('path');
const pathData = path.join(__dirname, '/abc','/qwer','../','/asdf');
console.log(pathData);
//输出结果:E:\file\abc\asdf
使用path.basename() 方法,可以获取路径中的做后一部分,经常通过这个方法获取路径中的文件名
path.basename(path[,ext]);
path:(string),必选参数,表示一个路径的字符串
ext:(string)可选参数,表示文件扩展名
返回值:(string)表示路径中的最后一部分
示例:
const filePath = 'E:\\file\\abc\\asdf.html';
const fullname1 = path.basename(filePath);
console.log(fullname1);
const fullname2 = path.basename(filePath,'.html');
console.log(fullname2);
//返回结果:
//asdf.html
//asdf
使用path.extname() 方法,可以获取路径中的扩展名部分
path.extname(path);
path:必选参数,表示一个路径的字符串
返回值:返回得到扩展名的字符串
示例:
const filePath = 'E:\\file\\abc\\asdf.html';
const fileExt = path.extname(filePath);
console.log(fileExt);
//返回结果:.html
http模块是用来创建web服务器的模块。通过http模块提供的 http.createServer() 方法,就能方便的把一台电脑变成一台web服务器,从而对外提供web资源服务
使用http模块创建web服务器,需先导入
const http = reqiure('http');
在node中不需要使用第三方web服务器,基于http模块通过代码手写一个服务器软件,从而对外提供web服务
IP地址就是互联网上每台计算机的唯一地址,因此IP地址具有唯一性,如果把“个人电脑”比作一台电话,“IP地址”就相当于“电话号码”,只有在知道对方IP的情况下才能与对应的电脑就行数据通信
IP地址的格式:通常用“点分十进制”表示成(a.b.c.d)的形式,其中,a,b,c,d都是0~255之间的十进制数。例:192.168.1.1
注意:
互联网中每台web服务器都有自己的IP地址,在Windows系统中 可以在终端运行ping 网址查看服务器的IP
尽管 IP 地址能够唯一地标记网络上的计算机,但IP地址是一长串数字,不直观,而且不便于记忆,于是人们又发明了另一套字符型的地址方案,即所谓的域名(Domain Name)地址。
IP地址和域名是一一对应的关系,这份对应关系存放在一种叫做域名服务器(DNS,Domain name server)的电脑中。使用者只需通过好记的域名访问对应的服务器即可,对应的转换工作由域名服务器实现。因此,域名服务器就是提供 IP 地址和域名之间的转换服务的服务器。
注意:
单纯使用 IP 地址,互联网中的电脑也能够正常工作。但是有了域名的加持,能让互联网的世界变得更加方便。
在开发测试期间, 127.0.0.1 对应的域名是 localhost,它们都代表我们自己的这台电脑,在使用效果上没有任何区别。
计算机中的端口号,就好像是现实生活中的门牌号一样。通过门牌号,外卖小哥可以在整栋大楼众多的房间中,准确把外卖送到你的手中。
同样的道理,在一台电脑中,可以运行成百上千个 web 服务。每个 web 服务都对应一个唯一的端口号。客户端发送过来的网络请求,通过端口号,可以被准确地交给对应的 web 服务进行处理。
注意:
每个端口号不能同时被多个 web 服务占用。
在实际应用中,URL 中的 80 端口可以被省略。
如果希望在自己的电脑上创建一个 web 服务器,从而对外提供 web 服务,则需要导入 http 模块
const http = require('http');
调用 http.createServer() 方法,即可快速创建一个 web 服务器实例
const server = http.createServer();
为服务器实例绑定 request 事件,即可监听客户端发送过来的网络请求
server.on('request', (req, res) => {
console.log("被请求了。。。。。");
})
调用服务器实例的 .listen() 方法,即可启动当前的 web 服务器实例
server.listen(8088,()=> {
console.log("8088启动成功");
})
只要服务器接收到了客户端的请求,就会调用通过 server.on() 为服务器绑定的 request 事件处理函数。
如果想在事件处理函数中,访问与客户端相关的数据或属性,可以使用如下的方式:
server.on('request', (req) => {
console.log("被请求了。。。。。" + req.url);
})
在服务器的 request 事件处理函数中,如果想访问与服务器相关的数据或属性,可以使用如下的方式:
res是响应对象,它包含了与服务器相关的数据和属性
server.on('request', (req,res) => {
const str = `${req.url} 被请求了`;
//res.end()方法作用:向客户端发送指定内容,并结束这次请求的处理过程
res.end(str);
})
手动设置内容的编码格式(当调用 res.end() 方法,向客户端发送中文内容的时候,会出现乱码问题)
server.on('request', (req, res) => {
const str = `${req.url} 被请求了`;
res.setHeader('Content-Type','text/html;charset=utf-8');
res.end(str);
})
server.on( " request ' , function( req,res){
const url = req.url //获取请求的url地t址
let content = 'ch1>404 Not found!'
//设置默认的内容为 404 Not found
if (url === '/' || url === '/index.html') {
content = '首页
' //用户诘求的是首页
else if (url === '/about.html' ) {
content = '关丁页面
' //用户请求的其他页面
res.setHeader( 'Content-Type','text/html: charset=utf-8') //设置Content-Type响应头,防止中文乱码
res.end(content) //把内容发送给客户端
})
模块化是指解决一个复杂问题时,自上向下逐层把系统划分为若干模块的过程,对于整个系统来说,模块是可组合、分解和更换的单元。
把代码进行模块化拆分的好处:
模块化规范,降低了沟通成本,极大的方便了各个模块之间对的相互调用,利人利己
使用强大的 require() 方法,可以加载需要的内置模块、用户自定义模块、第三方模块进行使用
//加载内置模块
const fs = require('fs');
//加载用户自定义模块
const clock = require('./clock.js');
//加载第三方模块
//3.1 先下载 :在终端使用npm 或yarn 下载对应模块 npm i moment / yarn add moment
//3.2 加载
const moment = require('moment');
使用require()方法加载其他模块时,会执行被加载模块中的代码
1.什么是模块作用域
与函数作用域类似,在自定义模块中定义的变量方法等成员,只能在当前模块内被访问,这种模块级别的访问限制,叫做模块作用域。
//01.js
//在01.js模块作用域定义username
const username = '张三';
//在01.js模块作用域中定义函数
function hello(){
console.log("你好," + username);
}
//02.js 在02.js中引入01.js模块
const hello = require('./01.js');
//在02.js中无法访问01.js中的私有成员
console.log(hello);
//结果为空对象 {}
2.模块作用域的好处:防止全局变量污染的问题
1.module对象
在每个js文件自定义模块中都有一个module对象,它里面存储了和当前模块有关的信息
2.module.exports对象
用require导入模块时,导入的是该模块的exports属性
在一个自定义模块中,默认情况下,module.exports = {}
//向module.exports 对象上挂载属性
module.exports.username = '张三';
module.exports.hello = function() {
console.log('Hello');
};
3.向外共享成员时的注意点
使用require()方法导入模块时,导入的结果,永远以module.exports指向的对象为准
4.exports
由于 module.exports 单词写起来比较复杂,为了简化向外共享成员的代码,Node 提供了 exports 对象。默认情况下,exports 和 module.exports 指向同一个对象。最终共享的结果,还是以 module.exports 指向的对象为准。
本来默认 exports === module.exports,但是内容不同,最后导入时永远以module.exports为准
导入后模块中只有 {gender:'男',age:'22'}
导入后模块中只有{username:'zs'}
导入后模块中有{username:'zs',gender:'男'}
导入后模块中有{username:'zs',gender:'男',age:'22'}
注意:为了防止混乱,建议大家不要在同一个模块中同时使用 exports 和 module.exports
CommonJS 规定:
node.js中的第三方模块又叫做包
由第三方个人或团队开发出来的,免费共所有人使用
由于 Node.js 的内置模块仅提供了一些底层的 API,导致在基于内置模块进行项目开发的时,效率很低。
包是基于内置模块封装出来的,提供了更高级、更方便的 API,极大的提高了开发效率。
包和内置模块之间的关系,类似于 jQuery 和 浏览器内置 API 之间的关系。
国外有一家 IT 公司,叫做 npm, Inc. 这家公司旗下有一个非常著名的网站: https://www.npmjs.com/ ,它是全球最大的包共享平台,你可以从这个网站上搜索到任何你需要的包
npm, Inc. 公司提供了一个地址为 https://registry.npmjs.org/ 的服务器,来对外共享所有的包,我们可以从这个服务器上下载自己所需要的包。
注意:
从 https://www.npmjs.com/ 网站上搜索自己所需要的包
从 https://registry.npmjs.org/ 服务器上下载自己需要的包
npm, Inc. 公司提供了一个包管理工具,我们可以使用这个包管理工具,从 https://registry.npmjs.org/ 服务器把需要的包下载到本地使用。
这个包管理工具的名字叫做 Node Package Manager(简称 npm 包管理工具),这个包管理工具随着 Node.js 的安装包一起被安装到了用户的电脑上。
大家可以在终端中执行 npm -v 命令,来查看自己电脑上所安装的 npm 包管理工具的版本号
//time.js
//定义格式化时间的方法
function timeFormat(data) {
const time = new Date(data);
const y = time.getFullYear();
const m = padZero(time.getMonth()+1);
const d = padZero(time.getDate());
const hh = padZero(time.getHours());
const mm = padZero(time.getMinutes());
const ss = padZero(time.getSeconds());
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`;
}
//定义补0的操作
function padZero(data) {
return data > 9 ? data :'0' + data;
}
module.exports = {
timeFormat
};
//test.js
const time = require('./time')
const t = new Date()
console.log(t);
const T = time.timeFormat(t);
console.log(T);
//结果:
//2022-08-13T09:20:36.075Z
//2022-08-13 17:20:36
npm install 包的完整名称 || npm i 包的完整名称
//导入所需要的包
const moment = require('moment');
const t = new Date()
console.log(t);
//包的使用方法可去查官方文档
const T = moment(t).format('YYYY-MM-DD HH:mm:ss');
console.log(T);
//结果:
//2022-08-13T09:47:32.882Z
//2022-08-13 17:47:32
其中:
node_modules 文件夹用来存放所有已安装到项目中的包。require() 导入第三方包时,就是从这个目录中查找并加载包。
package-lock.json 配置文件用来记录 node_modules 目录下的每一个包的下载信息,例如包的名字、版本号、下载地址等。
注意:
程序员不要手动修改 node_modules 或 package-lock.json 文件中的任何代码,npm 包管理工具会自动维护它们。
npm i momen@2.22.2
其中每一位数字所代表的的含义如下:
第1位数字:大版本
第2位数字:功能版本
第3位数字:Bug修复版本
版本号提升的规则:只要前面的版本号增长了,则后面的版本号归零。
npm 规定,在项目根目录中,必须提供一个叫做 package.json 的包管理配置文件。用来记录与项目有关的一些配置信息。
整个项目的体积是4.03M
第三方包的体积是 4.03M
项目源代码的体积 几乎为0M
遇到的问题:第三方包的体积过大,不方便团队成员之间共享项目源代码。
解决方案:共享时剔除node_modules
在项目根目录中,创建一个叫做 package.json 的配置文件,即可用来记录项目中安装了哪些包。从而方便剔除 node_modules目录之后,在团队成员之间共享项目的源代码。
注意:今后在项目开发中,一定要把 node_modules 文件夹,添加到 .gitignore 忽略文件中。
npm init -y
Error: Cannot find module 'moment'
(项目运行依赖 moment 这个包)可以运行 npm install 命令(或 npm i)一次性安装所有的依赖包
执行npm install 命令l时,npm 包管理工具会先读取package.json 中的 dependencies 节点,读取到记录的所有依赖名称和版本号之后,npm包管埋工具会把这些包一次性下载到项目中
可以运行 npm uninstall 命令,来卸载指定的包
使用 npm uninstall 具体包名 来卸载包:npm uninstall moment
注意:
npm uninstall 命令执行成功后,会把卸载的包,自动从 package.json 的 dependencies 中移除掉。
devDependencies 节点
如果某些包只在项目开发阶段会用到,在项目上线之后不会用到,则建议把这些包记录到devDependencies 节点中。
与之对应的,如果某些包在开发和项目上线之后都需要用到,则建议把这些包记录到 dependencies 节点中。
可以使用如下的命令,将包记录到 devDependencies 节点中:
npm i 包名 -D
或npm install 包名 --save-dev
如果不知道是否会在项目上线后用到,可以去npm官网查看一下安装方法
扩展阅读 - 海底光缆:
淘宝在国内搭建了一个服务器,专门把国外官方服务器上的包同步到国内的服务器,然后在国内提供下包的服务。从而极大的提高了下包的速度。
扩展:
镜像(Mirroring)是一种文件存储形式,一个磁盘上的数据在另一个磁盘上存在一个完全相同的副本即为镜像。
查看当前的下包镜像源:
npm config get registry
将下包镜像源切换为淘宝镜像源:npm config set registry=http://registry.npm.taobao.org/
检查镜像源是否下载成功:npm config get registry
为了更方便的切换下包的镜像源,我们可以安装 nrm 这个小工具,利用 nrm 提供的终端命令,可以快速查看和切换下包的镜像源。
通过npm将nrm安装为全局可用的工具:
npm i nrm -g
查看所有可用镜像源:nrm ls
将下包镜像源切换为淘宝镜像源:nrm use taobao
使用 npm 包管理工具下载的包,共分为两大类,分别是:项目包、全局包
项目包又分为两类,分别是:
npm i 包名 -D
npm i 包名
npm i 包名 -g
卸载全局安装的包名:npm uninstall 包名 -g
注意:
使用步骤:
将 i5ting_toc 安装为全局包:npm install -g i5ting_toc
调用 i5ting_toc 轻松实现 md 转 html 的功能:i5ting_toc -f 要转换的md文件路径 -o
一个规范的包,它的组成结构,必须符合以下 3 点要求:
①包必须以单独的目录而存在
②包的顶级目录下要必须包含 package.json 这个包管理配置文件
③package.json 中必须包含 name,version,main 这三个属性,分别代表包的名字、版本号、包的入口。
以上 3 点要求是一个规范的包结构必须遵守的格式,关于更多的约束,可以参考如下网址:https://yarnpkg.com/zh-Hans/docs/package-json
模块在第一次加载后会被缓存。 这也意味着多次调用 require() 不会导致模块的代码被执行多次。
不论是内置模块、用户自定义模块、还是第三方模块,它们都会优先从缓存中加载,从而提高模块的加载效率
内置模块是由 Node.js 官方提供的模块,内置模块的加载优先级最高。
例如,require(‘fs’) 始终返回内置的 fs 模块,即使在 node_modules 目录下有名字相同的包也叫做 fs。
使用 require() 加载自定义模块时,必须指定以 ./ 或 …/ 开头的路径标识符。在加载自定义模块时,如果没有指定 ./ 或 …/ 这样的路径标识符,则 node 会把它当作内置模块或第三方模块进行加载。
在使用 require() 导入自定义模块时,如果省略了文件的扩展名,则 Node.js 会按顺序分别尝试加载以下的文件:
- 按照确切的文件名进行加载
- 补全 .js 扩展名进行加载
- 补全 .json 扩展名进行加载
- 补全 .node 扩展名进行加载
- 加载失败,终端报错
如果传递给 require() 的模块标识符不是一个内置模块,也没有以 ‘./’ 或 ‘…/’ 开头,则 Node.js 会从当前模块的父目录开始,尝试从 /node_modules 文件夹中加载第三方模块。
如果没有找到对应的第三方模块,则移动到再上一层父目录中,进行加载,直到文件系统的根目录。
当把目录作为模块标识符,传递给 require() 进行加载的时候,有三种加载方式:
使用 Express,我们可以方便、快速的创建 Web 网站的服务器或 API 接口的服务器。
npm i express@版本号
也可以全局安装,参考上边npm的使用
//1.导入express
const express = require('express');
//2.创建web服务器
const app = express();
//3.调用app.listen(端口号,启动成功后的回调函数),启动服务器
app.listen(8088,()=>{
console.log("express server is running......");
})
//参数一:客户请求的url地址
//参数二:请求处理对应的函数(req:请求对象,包含了与请求相关的属性与方法;res:响应对象,包含了与响应相关的属性与方法)
app.get('请求URL',function(req, res){/*处理函数*/})
app.post('请求URL',function(req, res){/*处理函数*/})
app.get('/user',(req,res)=>{
//向客户端发送JSON对象
res.send({name:'宋亚轩',age:'18',gerder:'男'})
})
app.post('/user',(req,res)=>{
//向客户端发送文本内容
res.send('请求成功')
})
app.get('/user',(req,res)=>{
//req.query默认是一个空对象
//客户端使用 ?name=zhangsan&age=20 这种字符串形式,
//发送到服务器的参数可以通过req.query对象访问到
console.log(req.query);
})
app.get('/user/:id',(req,res)=>{
//req.params 默认是一个空对象
//里面存放着通过 : 动态匹配到的参数值
console.log(req.params);
res.send(req.params);
})
app.get('/user/:id/:username/:age',(req,res)=>{
//req.params 默认是一个空对象
//里面存放着通过 : 动态匹配到的参数值
console.log(req.params);
res.send(req.params);
})
1.express.static()
express 提供了一个非常好用的函数,叫做 express.static(),通过它,我们可以非常方便地创建一个静态资源服务器,例如,通过如下代码就可以将 public 目录下的图片、CSS 文件、JavaScript 文件对外开放访问了:
app.use(express.static('静态资源文件夹路径')
注意:
Express 在指定的静态目录中查找文件,并对外提供资源的访问路径。
因此,存放静态文件的目录名不会出现在 URL 中。
2.托管多个静态资源目录
多次调用express.static()
该函数会根据目录的添加顺序查找所需的文件
3.挂载路径前缀
加了前缀之后,访问路径必须加上该前缀才能访问静态资源
app.use('/路径前缀',express.static('静态资源文件夹路径'))
npm install -g nodemon
app.请求类型(请求的URL地址,处理函数);
路由匹配的注意点:
抽离步骤:
- 创建路由模块对应的js文件
- 调用express.Router()函数创建路由对象
- 向路由对象上挂载具体的路由
- 使用moudule.exports 向外共享路由对象
- 使用app.use()函数注册路由模块
const express = require('express');
const router = express.Router();
router.get('/user/list',(req,res) => {
res.send('获取用户列表')
})
router.post('/user/add',(req,res) => {
res.send('添加用户')
})
module.exports = router
//1.导入路由模块
const userRouter = require('./router.js')
//2.使用 app.use() 注册路由模块
app.use(userRouter)
app.use('/api',userRouter)
//常量 mw 所指向的就是一个中间件函数
const mw = function(req, res, next) {
console.log("这是一个简单的中间件函数");
//注意:在当前中间件的业务处理完毕后,必须调用next 函数,表示把流转关系交给下一个中间件或路由
next();
}
//常量 mw 所指向的就是一个中间件函数
const mw = function(req, res, next) {
console.log("这是一个简单的中间件函数");
//注意:在当前中间件的业务处理完毕后,必须调用next 函数,表示把流转关系交给下一个中间件或路由
next();
}
app.use(mw);
//常量 mw 所指向的就是一个中间件函数
app.use(function(req, res, next) {
console.log("这是一个简单的中间件函数");
//注意:在当前中间件的业务处理完毕后,必须调用next 函数,表示把流转关系交给下一个中间件或路由
next();
})
app.use(function(req, res, next) {
console.log("调用了第1个中间件函数");
next();
})
app.use(function(req, res, next) {
console.log("调用了第2个中间件函数");
next();
})
app.get('/',function(req, res)=>{//请求这个路由,会依次触发上述两个全局中间件
res.send('Page')
})
const mw = function(req, res, next) {
console.log("这是一个简单的中间件函数");
next();
}
//mw这个中间件只在当前路由下生效,这种用法属于局部生效的中间件
app.get('/', mw,function(req, res) {
res.send('Page')
})
app.get('/',[mw1,mw2],function(req, res) {
res.send('Page')
})
app.get('/',mw1,mw2,function(req, res) {
res.send('Page')
})
8.了解中间件的五个使用注意事项
Express 官方把常见的中间件用法,分成了 5 大类,分别是:
通过 app.use() 或 app.get() 或 app.post() ,绑定到 app 实例上的中间件,叫做应用级别的中间件
绑定到 express.Router() 实例上的中间件,叫做路由级别的中间件。它的用法和应用级别中间件没有任何区别。只不过,应用级别中间件是绑定到 app 实例上,路由级别中间件绑定到 router 实例上
错误级别中间件的作用:
专门用来捕获整个项目中发生的异常错误,从而防止项目异常崩溃的问题。
格式:
错误级别中间件的 function 处理函数中,必须有 4 个形参,形参顺序从前到后,分别是 (err, req, res, next)。
注意: 错误级别的中间件,必须注册在所有路由之后
自 Express 4.16.0 版本开始,Express 内置了 3 个常用的中间件,极大的提高了 Express 项目的开发效率和体验:
非 Express 官方内置的,而是由第三方开发出来的中间件,叫做第三方中间件。在项目中,大家可以按需下载并配置第三方中间件,从而提高项目的开发效率。
例如:在 [email protected] 之前的版本中,经常使用 body-parser 这个第三方中间件,来解析请求体数据。使用步骤如下:
- 运行 npm install body-parser 安装中间件
- 使用 require 导入中间件
- 调用 app.use() 注册并使用中间件
注意:Express 内置的 express.urlencoded 中间件,就是基于 body-parser 这个第三方中间件进一步封装出来的。
自己手动模拟一个类似于 express.urlencoded 这样的中间件,来解析 POST 提交到服务器的表单数据。
实现步骤:
app.use(function(req, res, next) {
//中间件的业务逻辑
})
在中间件中,需要监听 req 对象的 data 事件,来获取客户端发送到服务器的数据。
如果数据量比较大,无法一次性发送完毕,则客户端会把数据切割后,分批发送到服务器。所以 data 事件可能会触发多次,每一次触发 data 事件时,获取到数据只是完整数据的一部分,需要手动对接收到的数据进行拼接。
//定义变量,用于储存客户端发过来的请求体数据
let str = "";
req.on(data, function(data) {
str += data.toString();
})
当请求体数据接收完毕之后,会自动触发 req 的 end 事件。
因此,我们可以在 req 的 end 事件中,拿到并处理完整的请求体数据。示例代码如下:
//监听req对象的end事件(请求体发送完毕之后自动触发)
req.on('end', function() {
console.log(str);
// 下边吧字符串格式的请求数据解析成对象格式
})
Node.js 内置了一个 querystring 模块,专门用来处理查询字符串。通过这个模块提供的 parse() 函数,可以轻松把查询字符串,解析成对象的格式。示例代码如下:
import qs = require('querystring');
// 下边吧字符串格式的请求数据解析成对象格式
const body = qs.parse(str);
上游的中间件和下游的中间件及路由之间,共享同一份 req 和 res。因此,我们可以将解析出来的数据,挂载为 req 的自定义属性,命名为 req.body,供下游使用。示例代码如下:
req.on('end', function() {
const body = qs.parse(str);
req.body = body;
next();
})
为了优化代码的结构,我们可以把自定义的中间件函数,封装为独立的模块,示例代码如下:
//bodyParse.js
function bodyParase(req, res, next) { /*其他代码*/}
module.exports = bodyParase
//1.导入
const bodyPaese = require('bodyParse')
//3.注册
app.use(bodyParse);
const express = require('express');
const app = express();
app.listen(8088,function(){
console.log("Express服务器8088正在运行...");
});
//router.js
const express = require('express');
const api = express.Router();
//写自己的路由
module.exports = api;
const express = require('express');
const app = express();
const api = require('./router.js');
app.use('/api',api);
app.listen(8088,function(){
console.log("Express服务器8088正在运行...");
});
api.get('/get', function(req, res) => {
//1.获取用户客户端通过查询字符串发送到服务器的数据
const query = req.query;
//2.调用res.send()方法,把数据相应给客户端
res.send({
status:200,
msg:'GET请求成功',
data:query
})
})
api.post('/post', function(req, res) => {
//1.获取用户客户端通过查询字符串发送到服务器的数据
const body = req.body;
//2.调用res.send()方法,把数据相应给客户端
res.send({
status:200,//200表示成功
msg:'POST请求成功',
data:body
})
})
刚才编写的 GET 和 POST接口,存在一个很严重的问题:不支持跨域请求。
解决接口跨域问题的方案主要有两种:
cors 是 Express 的一个第三方中间件。通过安装和配置 cors 中间件,可以很方便地解决跨域问题。
CORS (Cross-Origin Resource Sharing,跨域资源共享)由一系列 HTTP 响应头组成,这些 HTTP 响应头决定浏览器是否阻止前端 JS 代码跨域获取资源。
浏览器的同源安全策略默认会阻止网页“跨域”获取资源。但如果接口服务器配置了 CORS 相关的 HTTP 响应头,就可以解除浏览器端的跨域访问限制。