1. 初识node.js与内置模块
1.1 回顾与思考
浏览器中的javaScript的组成部分
- JS核心语法
- webAPI
为什么JavaScript可以再浏览器中被执行?
待执行的JS代码
JS解析引擎
不同的浏览器使用不同的JS解析引擎
为什么JS可以操作DOM还有BOM
每个浏览器中内置了DOM,BOM这样的API函数,因此浏览器中的javaScript才可以调用它们
浏览器中JS的运行环境
运行环境是指代码正常运行所需要的必要环境
Chrome浏览器运行环境:
V8引擎和内置API函数
- v8引擎负责解析和执行js代码
- 内置API是由运行环境提供的特殊接口,只能在所属的运行环境中被调用
** js能否做后端开发?**
1.2 Node.js简介
1.什么是Node.js
Node.js是一个基于Chrome V8引擎的JS运行环境
2.Node.js中的JS运行环境
V8引擎和内置API函数
- 浏览器是JS的前端运行环境
- Node.js是Js的后端运行环境
- Node.js中无法调用DOM和BOM等浏览器内置API
3. Node.js可以做什么?
4. Node.js学习路径
JS基础语法- Node.js内置API模块(fs,path,http)-第三方API模块(express,mysql)
1.3Node.js环境的安装
什么是终端?
终端是专门为开发人员设计的,用于实现人机交互的一种方式
1.windows+R
2.输入cmd
1.4在Node.js环境中执行JS代码
1.打开终端
直接在地址栏敲cmd,可以使cmd切换到当下文件的地址
还可以在当前目录下的空白处按住shift键,右键打开powerShell
2.输入node 要执行的js文件的路径
node 空格 文件名
终端中的快捷键
2.fs文件系统模块
2.1什么是文件系统模块
fs模块是Node.js官方提供的、用来操作文件的模块,它提供了一系列的方法和属性,用来满足用户对文件的操作需求
2.2读取指定文件中的内容
fs.readFile()的语法格式
使用fs.readFile()方法,可以读取指定文件中的内容,语法格式如下:
fs.readFile(path[,options],callback)
参数1:必选参数,字符串,表示文件的路径
参数2:可选参数,表示以什么编码格式来读取文件
参数3:必选参数,文件读取完成后,通过回调函数拿到读取的结果。
// 1.导入fs模块,来操作文件
const fs = require(‘fs’)
// 2.调用fs.readFile()方法读取文件
fs.readFile(‘./file/1.txt’, ‘utf8’, function (err, dataStr) {
// 打印失败的结果,如果读取成功,则err的值为null
// 如果读取失败,则err的值为错误对象,dataStr的值为undefined
console.log(err)
// 打印成功的结果
console.log(dataStr)})
判断文件是否读取成功
// 1.导入fs模块,来操作文件const fs = require('fs')// 2.调用fs.readFile()方法读取文件fs.readFile('./file/1.txt', 'utf8', function (err, dataStr) { // 打印失败的结果,如果读取成功,则err的值为null // 如果读取失败,则err的值为错误对象,dataStr的值为undefined if (err) { return console.log('读取文件失败' + err.message) } console.log('读取文件成功' + dataStr)})
2.3向指定的文件中写入内容
fs.writeFile()的语法格式
使用该方法,可以向指定的文件中写入内容,语法格式如下
fs.writeFile(file, data[, options], callback)
- 参数1:必选参数,字符串,表示文件的存放路径
- 参数2:必选参数,表示要写入的内容
- 参数3:可选参数,表示以什么编码格式来读取文件,默认值是utf8
- 参数4:必选参数,文件写入完成后的回调函数。
判断文件是否读取成功
案例:考试成绩整理
核心实现步骤
导入需要的fs文件系统模块
使用fs.readFile()方法,读取素材目录下的成绩.txt文件
判断文件是否读取失败
文件读取成功后,处理成绩数据
将处理完成的成绩数据,调用fs.writeFile()方法,写入到新文件中
// 导入fs模块
const fs = require(‘fs’)
// 2.调用fs.readFile()方法读取文件
fs.readFile(‘./file/2.txt’, ‘utf8’, function (err, dataStr) {
// 打印失败的结果,如果读取成功,则err的值为null
// 如果读取失败,则err的值为错误对象,dataStr的值为undefined
// 3.判断文件是否读取成功
if (err) {
return console.log(‘读取文件失败’ + err.message)
}
console.log(‘读取文件成功’ + dataStr)// 4.1先把成绩的数据,按照空格进行分割const arrOld = dataStr.split(' ')// 4.2循环分割后的数组,对每一项的数据,进行字符串的替换操作const arrNew = []arrOld.forEach(item => { arrNew.push(item.replace('=', ':'))})console.log(arrNew)// 4.3把新数组中的每一项,进行合并,得到新的字符串const newStr = arrNew.join('
‘)
console.log(newStr)
// 5.调用fs.writeFile()方法,把处理完毕的成绩,写入到新文件中
fs.writeFile(’./file/成绩ok.txt’, newStr, function (err) {
if (err) {
return console.log(‘写入文件失败!’ + err.message)
}
console.log(‘写入文件成功!’)
})
})
2.4路径问题
3 path路径模块
path模块是Node.js官方提供的,用来处理路径的模块,它提供一系列的方法和属性,用来满足用户对于路径的处理需求
如果要在JS代码中,使用path模块来处理路径,则需要使用以下的方式先导入它
const path=require('path')
3.1 path.join()语法格式
使用path.join方法,可以把多个路径片段拼接为较为完整的路径字符串
path.join([...paths])
注意:今后凡是涉及路径拼接的操作,都是使用path.join()方法进行处理,不要直接使用+进行字符串的拼接
3.2 path.basename()
使用该方法可以获取路径中的最后一部分,经常通过这个方法获取路径中的文件名,语法格式如下
path.basename(path[,ext])
参数:path:必选参数,表示一个路径的字符串
ext可选参数,表示文件拓展名
返回:表示路径中的最后一部分
3.3 path.extname()
使用该方法可以获取路径中的拓展名部分,语法格式如下
path.extname(path)
path:必选参数,表示一个路径的字符串
返回:返回得到的拓展名字符串
案例:时钟
// 1.1 导入需要的模块并创建正则表达式const fs=require('fs')// 1.2导入path路径处理模块const path=require('path')// 1.3匹配标签// 其中s表示空白字符,S表示非空白字符,*表示匹配任意次const regStyle=/[sS]*/// 1.4匹配标签的正则const regScript=/[sS]*/
注意
1.fs.write()方法只能用来创建文件,不能用来创建路径
2.重复调用fs.writeFile()只能写入同一个文件,新写入的内容会覆盖之前的旧内容
4.http模块
4.1 基础概念
什么是客户端,什么是服务器?
在网络节点中,负责消费资源的电脑,叫做客户端。
负责对外提供网络资源的电脑,叫做服务器。
Http模块是Node.js官方提供的,用来创建web服务器的模块。通过http模块提供的Http.createServer()方法,就能方便的把一台普通电脑,变成一台web服务器,从而对外提供Web资源服务。
const http=require('http')
4.2 创建最基本的web服务器
1.导入Http模块
const http=require('http')
2.创建web服务器实例
const server=http.createServer()
3.为服务器实例绑定request事件,监听客户端的请求
//使用服务器实例的.on()方法,为服务器绑定一个request事件server.on('request',(req,res)=>{//只要有客户端来请求我们自己的服务器,就会触发request事件,从而调用这个事件处理函数console.log('someone visit our web server.')})
4.启动服务器
//调用server.listen(端口号,cb回调)方法,即可启动web服务器server.listen(80,()=>{console.log('http server running at http://127.0.0.1')})
4.3 req请求对象
只要服务器接收到了客户端的请求,就会调用server.on()为服务器绑定的request事件处理函数
如果想在事件处理函数中,访问与客户端相关的数据或属性,可以使用如下的方式:
server.on('request',(req,res)=>{//req是请求对象,它包含了与客户端相关的数据和属性,例如://req.url是客户端相关的数据和属性,例如://req.url是客户端请求的url地址//req.method 是客户端的method请求类型const str='Your request url is ${req.url},and request method is ${req.method}'console.log(str)})
4.4 res响应对象
server.on('request',(req,res)=>{//res是响应对象,它包含了与服务器相关的数据和属性,例如://要发送到客户端的字符串const str='Your request url is ${req.url},and request method is ${req.method}'//res.end()方法的调用://向客户端发送指定内容,并结束这次请求的处理过程res.end(str)})
4.5解决中文乱码问题
当调用res.end()方法,向客户端发送中文内容的时候,会出现乱码问题,此时,需要手动设置内容的编码格式
server.on('request',(req,res)=>{//发送的内容包含中文conststr='您请求的url地址是${req.url},请求的method类型是${req.method}'//为了防止中文显示乱码的问题,需要设置响应头res.setHeader('Content-Type','text/html;charset=utf-8')//把包含中文的内容,响应给客户端res.end(str)})
4.6根据不同的url响应不同的html内容
server.on('request',function(req,res){const url =req.url //1.获取请求的Url地址let content ='404 Not found!
'//2.设置默认的内容if(url=='/'||url ==='/index.html'){content='首页
'//3.用户请求的是首页}else if(url==='/about.html'){content='关于页面
'}//为了防止中文显示乱码的问题,需要设置响应头res.setHeader('Content-Type','text/html;charset=utf-8')//把包含中文的内容,响应给客户端res.end(str)})
5.模块化
5.1 模块化的基本概念
什么是模块化?
模块化是指解决一个复杂问题时,自顶向下逐层把系统划分为若干模块的过程。对于整个系统来说,模块是可组合、分解和变换的单元
编程领域中的模块化:就是遵守固定的规则,把一个大文件拆成独立并且互相依赖的多个小模块。
模块化规范
就是对代码进行模块化的拆分与组合时,需要遵守的规则
使用什么样的语法格式来引用模块
在模块中使用什么样的语法格式向外暴露成员
5.2Node.js中的模块化
根据模块来源的不同,将模块分为了三大类,分别是:
内置模块(fs,path,http)
自定义模块(用户创建的每个Js文件)
第三方模块(由第三方开发出来的模块)
什么是模块作用域?
和函数作用域类似,在自定义模块中定义的变量、方法等成员,只能在当前模块内被访问,这种模块级别的访问限制,叫做模块作用域
防止了全局变量污染的问题
向外共享模块作用域中的成员
module对象:在每个·.js自定义模块中都有一个module对象,它里面存储了和当前模块有关的信息
module.export对象
可以使用该对象将模块内的成员共享出去,供外界使用。外界用require方法导入自定义模块时,得到的就是Module.exports所指的对象
exports对象
由于module.exports单词写起来比较复杂,为了简化向外共享成员的代码,Node提供了exports对象。默认情况下,exports和module.exports指向同一个对象,最终共享的结果,还是以module.exports指向的结果为准
exports和module.exports的使用误区
模块化规范
5.3 npm与包
包的基本概念
Node.js中的第三方模块又叫做包
从哪里搜索包:全球最大的包共享平台
从哪里下载包:下载包的服务器
如何下载包?npm包管理工具
格式化时间的高级做法
1.使用npm包管理工具,在项目中安装格式化时间的包moment
2.使用require()导入格式化时间的包
3.参考Moment的官方API文档对时间进行格式化
//1.导入Moment包,注意导入的名称,就是装包时候的名称const moment = require('moment')//注意是字符串node 6 const dt = moment().format('YYYY-MM-DD HH:mm:ss')console.log(dt)
在项目中安装包的命令
npm install 包的完整名称
简写形式
npm i 包的完整名称
注意点
1.初次装包完成后,在项目文件夹下多一个叫做node_modules的文件夹和package-lock.json的配置文件,其中node_modules文件夹用来存放所有已安装到项目中的包。require()导入第三方包时,就是从这个目录中查找并加载包
package-lock.json配置文件用来记录node_modules目录下的每一个包的下载信息,例如包的版本号,下载地址等
2.安装指定版本的包:
默认情况下,会自定安装最新版本的包,如果需要安装指定版本的包,可以在包名之后,通过@符号指定具体的版本
npm i moment@2.22.2
3.包的语义化版本规范
包的版本号是以“点分十进制”形式进行定义的,总共有三位数字,例如2.24.0
其中每一位数字所代表的含义如下:第一位:大版本
第二位:功能版本 第三位:bug修复版本
只要前面的版本号增长了,则后面的版本号归零
包管理配置文件
1.多人协作的问题:第三方的包的体积过大,不方便成员之间共享项目源代码
解决方案:共享时剔除node_modules
2.在项目根目录中,创建一个叫做package.json的配置文件,即可用来记录在项目中安装了哪些包,从而方便剔除node_modules目录之后,在团队成员之间共享项目的源代码
今后在项目开发中,一定要把Node_modules文件夹,添加到.gitignore忽略文件夹中
3.npm包管理工具提供了一个快捷命令,可以在执行命令时所处的目录中,快速创建package.json这个包管理配置文件:
npm init -y
上述命令只能在英文的目录下运行成功!所以项目文件夹的名称一定要使用英文命名,不能出现空格。运行npm install命令安装包的时候,npm包管理工具,会自动把包的名称和版本号,记录到package.json中
4.一次性安装所有包
可以运行npm install命令,一次性安装所有安装所有的依赖包
//执行npm install 命令时,npm 包管理工具会先读取package.json中的dependencies节点//读取到记录的所有依赖包名称和版本号之后,包管理工具会把这些包一次性的下载到项目中去npm install
5.卸载包
可以npm uninstall
//使用npm uninstall 具体的包名 来卸载包npm uninstall moment
注意 npm uninstall命令执行成功后,会把卸载的包,自动从package.json的dependencies中移除掉
解决下包速度慢的问题
淘宝NPM镜像服务器
镜像:是一种文件存储形式每一个磁盘上的数据在另一个磁盘上存在完全相同的副本即为镜像
切换npm的下包镜像源。下包的镜像源,指的就是下包的服务器地址
npm config get registrynpm config set registry=https://registry.npm.taobao.org/
nrm
为了更方便的切换下包镜像源,我们可以安装nrm这个小工具,利用nrm提供的终端命令,可以快速查看和切换下包的镜像源
//通过npm包管理工具,将nrm安装为全局可用的工具npm i nrm -g//查看所有能用的镜像原nrm ls//将下包的镜像原切换为taobao镜像nrm use taobao
包的分类
项目包:那些被安装到项目的node_modules目录中的包,都是项目包
项目包又分为两类,分别是开发依赖包和核心依赖包
npm i 包名-D //开发依赖包(会被记录到devDependencies节点下)npm i 包名//核心依赖包(会被记录到)
1.全局包
npm i包名-g //全局安装指定的包npm uninstall 包名-g//卸载全局安装的包
注意:只要工具性质的包,才有全局安装的必要性,因为它们提供了很好的终端命令
判断某个包是否需要全局安装后才能使用,可以参考官方提供的使用说明即可
2.i5ting_toc
i5ting_toc是一个可以把md文档转为Html页面的小工具,使用步骤如下
//将i5ting_toc安装为全局包npm install -g i5ting_toc//调用,轻松实现md转换为html的功能i5ting_toc -f 要转换的md文件路径 -o
3.规范的包结构
开发属于自己的包
1.需要实现的功能
格式化日期
转义Html中的特殊字符
还原html中的特殊字符
5.4 模块的加载机制
优先从缓存中加载
模块在第一次被加载后会被缓存。这也意味着多次调用require()不会导致模块的代码被多次执行。注意:不论是内置模块,自定义模块,还是第三方模块,它们都会优先从缓存中加载,从而提高模块的加载效率。
内置模块的加载机制
内置模块加载优先级最高。
自定义模块的加载机制
使用require()加载自定义模块时,必须指定以./或…/开头的路径标识符。在加载自定义模块时,如果没有指定的话,则node模块就会把他当成内置模块或者第三方模块进行加载
同时,在使用require()导入自定义模块时,如果省略了文件的拓展名,则node.js就会按顺序分别尝试加载以下的文件
1.按照确切的文件名进行加载
2.补全.js拓展名进行加载
3.补全.json拓展名进行加载
4.补全.node拓展名进行加载
5.加载失败,终端报错
第三方模块的加载机制
如果传递给require()的模块标识符不是一个内置模块,也没有./等等开题,则Node.js会从当前模块的父目录开始,尝试从.node_modules文件夹中加载第三方模块。如果没有找到第三方模块,则移动到再上一层父目录中,进行加载,直到文件系统的根目录
目录作为模块
6.express
6.1express简介
Express是基于Node.js平台,快速,开放,极简的Web开发框架
思考:不使用Express能否创建Web服务器?
能,使用Node.js提供原生的http模块即可
但是http内置模块用起来很复杂,Express是基于内置的http模块进一步封装起来的,能够极大的提高开发效率
express的安装
在项目所处的目录中,运行如下的终端命令,即可将express安装到项目中使用:
npm i express@4.17.1
使用express创建最基本的服务器
//1.导入expressconst express = require('express')//2.创建web服务器const app = express()//3.启动web服务器app.listen(80, () => { console.log('express server running at http://127.0.0.1')})
6.2 Express的基本使用
监听GET请求
通过app.get()方法,可以监听客户端的GET请求,具体的语法格式如下:
//参数1:客户端请求的url地址//参数2:请求对应的处理函数//req:请求对象(包含了与请求属性相关的属性与方法)//res:响应对象(包含了与响应对象相关的属性与方法)app.get('请求url',function(req,res){/*处理函数*/})
监听POST请求
通过app.post()方法,可以监听客户端的POST请求,具体的语法格式如下:
//参数1:客户端请求的url地址//参数2:请求对应的处理函数//req:请求对象(包含了与请求属性相关的属性与方法)//res:响应对象(包含了与响应对象相关的属性与方法)app.post('请求url',function(req,res){/*处理函数*/})
把内容响应给客户端
通过res.send()方法,可以把处理好的内容,发送给客户端
app.get('/user',(req,res)=>{//向客户发送json请求res.send({name:'zs',age:20,gender:'男'})})app.post('/user',(req,res)=>{//向客户端发送文本内容res.send('请求帮助')})
获取URL中携带的查询参数
通过req.query对象,可以访问到客户端通过查询字符串的形式,发送到服务器的参数:
app.get('/', (req, res) => { //通过req.query可以获取到客户端发送过来的查询参数 //注意:在默认情况下,req.query是一个空对象 //客户端使用?name=zs&age=20这种查询字符串形式,发送到服务器参数 //可以通过req.query对象访问到,例如: //req.query.name req.query.age console.log(req.query) res.send(res.query)})
获取URL的动态参数
通过req.params对象,可以访问到Url中,通过:匹配到的动态参数
//url地址中,我们开业通过:参数名的形式,匹配动态参数值app.get('user/:id',(req,res)=>{//req.params默认是一个空对象//里面存放这通过:动态匹配到的参数值console.log(req.params)})
汇总
//1.导入expressconst express = require('express')const res = require('express/lib/response')//2.创建web服务器const app = express()//4.监听客户端的GET和POST请求,并向客户端响应具体的内容app.get('/user', () => { //调用express提高的res.send()方法,向客户端响应一个json对象 res.send({ name: 'zs', age: 20, gender: '男' })})app.post('/user', (req, res) => { //调用express提供的res.send()方法,向客户端响应一个文本字符串 res.send('请求成功')})app.get('/', (req, res) => { //通过req.query可以获取到客户端发送过来的查询参数 //注意:在默认情况下,req.query是一个空对象 console.log(req.query) res.send(res.query)})//url地址中,我们开业通过:参数名的形式,匹配动态参数值app.get('user/:id', (req, res) => { //req.params默认是一个空对象 //里面存放这通过:动态匹配到的参数值 console.log(req.params) res.send(req.params)})//3.启动web服务器app.listen(80, () => { console.log('express server running at http://127.0.0.1')})
6.3 托管静态资源
6.4 nodemon
当基于Node.js编写了一个网站应用的时候,传统的方式,是运行node.app.js命令,来启动项目。这样做的坏处是:当代码被修改之后,需要手动重启项目。
现在,我们可以node命令替换为nodemon命令,使用nodemon app.js来启动项目,这样做的好处是:代码被修改之后,会被nodemon监听到,从而实现自动重启项目的效果
node app.js//将上面的终端命令,替换为下面的终端命令,即可实现自动重启项目的效果nodemon app.js
7.express 路由
7.1 路由的概念
广义上来讲,路由就是映射关系
在Express中,路由指的是客户端请求与服务器处理函数之间的映射关系
Express中的路由分三部分组成,分别是请求的类型,请求的URL地址,处理函数。格式如下:
app.METHOD(PATH,HANDLER)
express中路由的例子
//匹配GET请求,且请求URL为/app.get('/',function(req,res){res.send('Hello World!')})//匹配post请求,且请求URL为/app.post('/',function(req,res){res.send('Got a POST request')})
路由的匹配过程
路由的使用
在express中使用路由最简单的方式,就是把路由器挂载到APP上,示例代码如下
const express=require('express')//创建web服务器,命名为appconst app= express()//挂载路由app.get('/',(req,res)=>{ res.send('hello world')})app.post('/',(req,res)=>{ res.send('Post Request.')})//启动web服务器app.listen(80,()=>{ console.log('http://127.0.0.1')})
7.2 路由的使用
模块化路由:为了方便对路由进行模块化的管理,Express不建议将路由直接挂载到app上,而是推荐将路由抽离为单独的模块。将路由抽离为单独模块的步骤如下:
1.创建路由模块对应的.js文件
2.调用express.Router()函数创建路由对象
3.向路由对象挂载具体的路由
4.使用module.exports向外共享路由对象
5.使用app.use()函数注册路由模块
创建路由模块
// 这是路由模块// 1. 导入 expressconst express = require('express')// 2. 创建路由对象const router = express.Router()// 3. 挂载具体的路由router.get('/user/list', (req, res) => { res.send('Get user list.')})//获取挂载用户列表的路由router.post('/user/add', (req, res) => { res.send('Add new user.')})//获取添加用户的路由// 4. 向外导出路由对象module.exports = router
注册路由模块
// 1. 导入路由模块const router = require('./03.router')// 2. 注册路由模块app.use(router)
为路由模块添加前缀
// 1. 导入路由模块const router = require('./03.router')// 2. 注册路由模块,使用app.use()注册路由模块,并添加统一的访问前缀/apiapp.use('/api', userrouter)
全部代码
const express = require('express')const app = express()// app.use('/files', express.static('./files'))// 1. 导入路由模块const router = require('./03.router')// 2. 注册路由模块app.use('/api', router)// 注意: app.use() 函数的作用,就是来注册全局中间件app.listen(80, () => { console.log('http://127.0.0.1')})
8 express中间件
8.1 Express中间件的概念
8.2 中间件的初体验
定义中间件函数
//常量mv所指向的,就是一个中间价函数const mv=funcntion(req,res,next){console.log('这是一个最简单的中间件函数')//注意:在当前中间价的业务处理完毕后,必须调用next()函数//表示把流转关系交给下一个中间价或路由器next()}
全局生效的中间件函数
客户端发起的任何请求,到达服务器之后,都会触发的中间件,叫做全局生效的中间价。通过调用app.use(中间件函数),即可定义一个全局生效的中间价,示例代码如下:
//全局生效的中间件app.use(mv)
定义全局中间件的简化形式
// 这是定义全局中间件的简化形式app.use((req, res, next) => { console.log('这是最简单的中间件函数') next()})
全部代码
const express = require('express')const app = express()// // 定义一个最简单的中间件函数// const mw = function (req, res, next) {// console.log('这是最简单的中间件函数')// // 把流转关系,转交给下一个中间件或路由// next()// }// // 将 mw 注册为全局生效的中间件// app.use(mw)// 这是定义全局中间件的简化形式app.use((req, res, next) => { console.log('这是最简单的中间件函数') next()})app.get('/', (req, res) => { console.log('调用了 / 这个路由') res.send('Home page.')})app.get('/user', (req, res) => { console.log('调用了 /user 这个路由') res.send('User page.')})app.listen(80, () => { console.log('http://127.0.0.1')})
中间件的作用
const express = require('express')const app = express()// 这是定义全局中间件的简化形式app.use((req, res, next) => { // 获取到请求到达服务器的时间 const time = Date.now() // 为 req 对象,挂载自定义属性,从而把时间共享给后面的所有路由 req.startTime = time next()})app.get('/', (req, res) => { res.send('Home page.' + req.startTime)})app.get('/user', (req, res) => { res.send('User page.' + req.startTime)})app.listen(80, () => { console.log('http://127.0.0.1')})
定义多个全局中间件
可以使用app.use()连续定义多个全局中间件,客户端请求到达服务器之后,会按照中间件定义的先后顺序依次进行调用,示例代码如下
const express = require('express')const app = express()// 定义第一个全局中间件app.use((req, res, next) => { console.log('调用了第1个全局中间件') next()})// 定义第二个全局中间件app.use((req, res, next) => { console.log('调用了第2个全局中间件') next()})// 定义一个路由app.get('/user', (req, res) => { res.send('User page.')})app.listen(80, () => { console.log('http://127.0.0.1')})
局部生效的中间件
不使用app.use()定义的中间件,叫做局部生效的中间件
// 导入 express 模块const express = require('express')// 创建 express 的服务器实例const app = express()// 1. 定义中间件函数const mw1 = (req, res, next) => { console.log('调用了局部生效的中间件') next()}// 2. 创建路由app.get('/', mw1, (req, res) => { res.send('Home page.')})app.get('/user', (req, res) => { res.send('User page.')})// 调用 app.listen 方法,指定端口号并启动web服务器app.listen(80, function () { console.log('Express server running at http://127.0.0.1')})
定义多个局部中间件
可以在路由中,通过如下两种等价的方式,使用多个局部中间件
// 导入 express 模块const express = require('express')// 创建 express 的服务器实例const app = express()// 1. 定义中间件函数const mw1 = (req, res, next) => { console.log('调用了第一个局部生效的中间件') next()}const mw2 = (req, res, next) => { console.log('调用了第二个局部生效的中间件') next()}// 2. 创建路由app.get('/', [mw1, mw2], (req, res) => { res.send('Home page.')})app.get('/user', (req, res) => { res.send('User page.')})// 调用 app.listen 方法,指定端口号并启动web服务器app.listen(80, function () { console.log('Express server running at http://127.0.0.1')})
了解中间件的注意事项
8.3 中间件的分类
// 导入 express 模块const express = require('express')// 创建 express 的服务器实例const app = express()// 1. 定义路由app.get('/', (req, res) => { // 1.1 人为的制造错误 throw new Error('服务器内部发生了错误!') res.send('Home page.')})// 2. 定义错误级别的中间件,捕获整个项目的异常错误,从而防止程序的崩溃app.use((err, req, res, next) => { console.log('发生了错误!' + err.message) res.send('Error:' + err.message)})// 调用 app.listen 方法,指定端口号并启动web服务器app.listen(80, function () { console.log('Express server running at http://127.0.0.1')})
错误级别的中间件,放到所有路由之后
// 导入 express 模块const express = require('express')// 创建 express 的服务器实例const app = express()// 注意:除了错误级别的中间件,其他的中间件,必须在路由之前进行配置// 通过 express.json() 这个中间件,解析表单中的 JSON 格式的数据app.use(express.json())// 通过 express.urlencoded() 这个中间件,来解析 表单中的 url-encoded 格式的数据app.use(express.urlencoded({ extended: false }))app.post('/user', (req, res) => { // 在服务器,可以使用 req.body 这个属性,来接收客户端发送过来的请求体数据 // 默认情况下,如果不配置解析表单数据的中间件,则 req.body 默认等于 undefined console.log(req.body) res.send('ok')})app.post('/book', (req, res) => { // 在服务器端,可以通过 req,body 来获取 JSON 格式的表单数据和 url-encoded 格式的数据 console.log(req.body) res.send('ok')})// 调用 app.listen 方法,指定端口号并启动web服务器app.listen(80, function () { console.log('Express server running at http://127.0.0.1')})
// 导入 express 模块const express = require('express')// 创建 express 的服务器实例const app = express()// 1. 导入解析表单数据的中间件 body-parserconst parser = require('body-parser')// 2. 使用 app.use() 注册中间件app.use(parser.urlencoded({ extended: false }))// app.use(express.urlencoded({ extended: false }))app.post('/user', (req, res) => { // 如果没有配置任何解析表单数据的中间件,则 req.body 默认等于 undefined console.log(req.body) res.send('ok')})// 调用 app.listen 方法,指定端口号并启动web服务器app.listen(80, function () { console.log('Express server running at http://127.0.0.1')})
8.4 自定义中间件
定义中间件
使用app.use()来定义全局生效的中间件,代码如下
app.use((req, res, next) => { // 定义中间件具体的业务逻辑 })
监听req的data事件
监听req的end事件
使用querystring模块解析请求体数据
将解析出来的数据对象挂载为req.body
完整代码
// 导入 express 模块const express = require('express')// 创建 express 的服务器实例const app = express()// 导入 Node.js 内置的 querystring 模块const qs = require('querystring')// 这是解析表单数据的中间件app.use((req, res, next) => { // 定义中间件具体的业务逻辑 // 1. 定义一个 str 字符串,专门用来存储客户端发送过来的请求体数据 let str = '' // 2. 监听 req 的 data 事件 req.on('data', (chunk) => { str += chunk }) // 3. 监听 req 的 end 事件 req.on('end', () => { // 在 str 中存放的是完整的请求体数据 // console.log(str) // TODO: 把字符串格式的请求体数据,解析成对象格式 const body = qs.parse(str) req.body = body next() })})app.post('/user', (req, res) => { res.send(req.body)})// 调用 app.listen 方法,指定端口号并启动web服务器app.listen(80, function () { console.log('Express server running at http://127.0.0.1')})
将自定义中间件封装为模块
// 导入 express 模块const express = require('express')// 创建 express 的服务器实例const app = express()// 1. 导入自己封装的中间件模块const customBodyParser = require('./14.custom-body-parser')// 2. 将自定义的中间件函数,注册为全局可用的中间件app.use(customBodyParser)app.post('/user', (req, res) => { res.send(req.body)})// 调用 app.listen 方法,指定端口号并启动web服务器app.listen(80, function () { console.log('Express server running at http://127.0.0.1')})
9.使用express写接口
9.1 创建基本的服务器
// 导入 expressconst express = require('express')// 创建服务器实例const app = express()// 启动服务器app.listen(80, () => { console.log('express server running at http://127.0.0.1')})
9.2 创建API路由模块
9.3编写GET接口
9.4 编写Post接口
9.5CORS跨域
接口的跨域问题:刚才编写的GET和POST请求,存在一个很严重的问题:不支持跨域请求。
解决接口跨域问题的方案主要有两种:
1.CORS(主流的方案,推荐使用)
2.JSONP(有缺陷的解决方案:只支持GET请求)
使用cors中间件解决跨域问题
CORS请求的分类
客户端在请求CORS接口时,根据请求方式和请求头的不同,可以将CORS的请求分为两大类,分别是:
1.简单请求
2.预检请求
9.6 JSONP接口