一、初识Express
1.什么是Express
Express是基于Node.js平台,快速、开放、极简的Web开发框架。(也就是Express和Node.js内置的http类似,都是用来创建Web服务器的)
Express本质上就是一个第三方包,提供了快速创建Web服务器的便捷方法。
(1)那既然和Node.js里的http作用一样,为什么还创造Express?
(第三方包的作用)内置模块开发效率低,用起来复杂
(2)http和Express的区别?
类似于浏览器中的web api和jquery的区别,后者是基于前者封装出来的
2.Express的作用
对于前端来说,最常见的两种服务器:
- Web网站服务器:专门对外提供Web网页资源的服务器
- API接口服务器:专门提供API接口的服务器(后端接口地址)
使用Express,可以快速的创建Web网站服务器或API接口服务器
二、Express的使用
1.安装
npm i express
2.使用
const express = require('express')
// 创建web服务器
const app=express()
// 启动web服务器
app.listen(80,()=>{
console.log('服务器运行在127.0.0.1')
})
来回顾一下http的写法(目前感觉没有什么区别。)

3.监听get请求
app.get()可以监听get请求
app.get('请求的url',function(req,res){
console.log(req,res,"88")
})
4.监听post请求
app.post('/',function(req,res){
console.log('有人对根目录发post请求')
})
5.把内容响应给客户端
res.send(),这次不是app.xx了,app是服务器对象,res代表当前的响应
app.get('/',function(req,res){
console.log(req,res,"88")
res.send('给你')
})
6.获取url中携带的参数查询
通过req.query,可以访问到客户端通过访问字符串的方式,发送到服务器的参数
app.get('/index.html',function(req,res){
// console.log(req,res,"88")
console.log(req.query)
res.send('给你')
})


如果访问链接的时候没有参数的话默认req.query是空对象
7.获取url中的动态参数
通过req.params对象,可以访问到url中,通过:匹配到的动态参数:
app.get('/user/:id',function(req,res){
console.log(req.params)
})
:id就成了动态变化的了,后面还可以继续拼 /user/:id/:name,拼出来之后拿到的就是一个对象里两个参数id和name(注意如果拼两个的话id和name就都得传


我最开始想的是?id=11这种,搜了一下是路径参数(直接拼值)和查询参数(?+变量+值)
路径参数适用于唯一标识的,获取特定资源。查询参数适用于嵌套资源关系和可选参数
三、托管静态资源
1.express.static()
express提供了一个非常好用的函数express.static(),让我们可以很方便的创建一个静态资源服务器,下面这段代码可以帮我们把public下的css、图片等对外开放
app.use(express.static('public'))
然后就可以直接访问图片了,不写public是因为只要在express.static写了一个文件夹名称,这个名称之后就不会出现在访问路径里了
这里面的路径是相对于命令执行的目录下的,./public相当于public(注意不要写/,容易被当作电脑根目录下的)

a.如果不写这句话为什么拿不到?
不写这句话的话浏览器不能直接去拿硬盘的东西
b.但是我的服务器不是已经开启了吗?为什么还拿不到?
开启了但是并不是什么都对外暴露,需要特定规则,比如输入了127.0.0.1就是请求到了服务器上,但是输入/11/lisa再由后端判断到上面拿什么东西
2.托管多个静态资源目录
如果要托管多个静态资源目录的话就调用多次express.static就可以了,而且如果托管了多个的话,因为托管的目录不会写在访问路径里,所以如果写了xxx/index.html的话,就先去写在前面的托管文件夹找,找不到的话再去第二个找、、
3.挂载路径前缀
如果希望在访问静态资源之前加上被托管的文件夹名称,就用下面这句代码
app.use('/public',express.static('public'))
这样寻找静态资源的时候就可以在前面加/public了
四、nodemon
1.为什么要使用nodemon
在编写调试node.js的时候,如果修改了代码就得关掉服务器重启非常麻烦,使用nodemon工具可以监听代码的变动,重启项目和调试。
2.安装nodemon
安装命令:
npm install -g nodemon
3.使用nodemon
之前我们启动服务都是node xx.js,现在改成用nodemon xx.js,现在再启动之后修改代码保存之后就重新调试了。
五、路由
1.什么是路由
广义上来说,路由就是映射关系。
2.express中的路由
在express中,路由指的是客户端的请求与服务器处理函数之间的映射关系。
express中的路由由三部分组成:请求的类型、请求的URL地址、处理函数,格式如下:
app.METHOD(PATH,HANDLER)
上面的那些app.get('/xx',function(req,res){}) / app.post('/xx',function(req,res){})都是路由
3.路由的匹配过程
每当一个请求到达服务器之后,都先进行路由的匹配,匹配成功之后再去调用对应的函数处理,在匹配的时候会先根据method和path进行匹配,如果这两个同时匹配成功,则Express会将这次请求交给对应的function函数处理。
前端的路由也是输入一个路径去对应一个页面,跟express路由的区别是前端的路由不用请求服务端。

注意点:会按照先后顺序来匹配,比如同一method和path写了两个,那就先匹配最前面那个。
4.路由的使用
在express中使用路由最简单的方式就是把路由挂载到app上。
就像上面写的那样,但是这种方式不好,因为代码越多挂载的app越多
5.模块化路由
为了方便对路由进行模块化管理,express不建议将路由直接挂载到app上,而是建议将路由抽离为单独的模块,步骤:
(1)创建路由模块对应的js文件
(2)调用express.Router()创建路由对象(之前的express()返回的是服务器)
(3)在路由对象上挂载具体的路由
(4)使用module.exports向外共享路由对象
(5)使用app.use()函数注册路由模块
const express=require('express')
const routers=express.Router()
routers.get('/',function(req,res){
console.log('get请求来咯',req)
res.send('给你get')
})
routers.post('/',function(req,res){
console.log('post请求来咯',req)
res.send('给你post')
})
module.exports =router
6.注册路由模块
步骤:
(1)导入路由模块
(2)使用app.use注册路由模块
const routers=require('./express_router1')
const express=require('express')
const app=express()
// 启动web服务器
app.listen(80,()=>{
console.log('服务器运行在127.0.0.1:80')
})
app.use(routers)
引入第五步的页面并在当前页面启动(最开始没反应结果是我没启动服务器。。)
app那一套还是用之前的,只不过用app.use去注册多个模块(和vue好像,不过vue是这样的:const app = createApp(App))
不过我上面那段代码写的有点问题,app.use应该放在app.listen前面,因为你得先告诉他收到请求怎么怎么做然后再去开启
app.use的作用就是来注册全局中间件的,express.static 是 Express 内置的一个中间件函数
中间件就是发起请求和响应之间的任意节点,他是一个函数,可以对请求或响应做处理(先简单了解一下,后期还会解释)
7.为路由模块添加前缀
类似托管静态资源的时候统一加的前缀,也是写在app.use里
app.use('api',routers)
六、中间件
1.什么是中间件
中间件特指业务流程中的中间处理环节
2.中间件的调用流程
当一个中间件到达Express服务器之后,可以连续调用多个中间件,从而对这次请求进行预处理。在这些请求中,上一个中间件的输出会作为下一个中间件的输入。
3.Express中间件的格式
Express中间件本质上就是一个function处理函数
app.get('/',function(req,res,next){
next()
})
中间件函数的行参必须包括next(必须放在最后),而路由处理函数只需要req和res
4.next函数的作用
next函数是实现多个中间件连续调用的关键,表示把流转关系转交给下一个中间件
5.定义中间件函数
const zzj=function(req,res,next){
console.log('zzj')
next()
}
这就是一个最简单的中间件函数的实现,是函数,有next参数,并且接力了(next())
6.全局生效的中间件
客户端发起的任何请求到达服务器之后都会经过的中间件就是全局生效的中间件,通过app.use(中间件函数)就可以定义一个全局生效的中间件
const express=require('express')
const app=express()
const zzj=function(req,res,next){
console.log('zzj')
next()
}
app.use(zzj)
这样任何请求过来的时候都会输出zzj
简化形式:
app.use(function(req,res,next){
console.log('zzj')
next()
})
这样就省略了中间的变量
7.中间件的作用
多个中间件之间使用同一个req和res,然后我们就可以在上游的中间件给req/res加点属性之类的供下游去使用
app.use(function(req,res,next){
req.time=Date.now()
console.log('zzj',req.time)
next()
})
app.get('/',(req,res)=>{
console.log('走到了终点',req.time)
})
只有一个中间件并且进行了next(),我本来以为不对(我本来觉得next()之后后面必须还得有中间件去接才对),结果是next()是去寻找下一个能匹配的路由,只要能匹配到就行。
定义多个全局中间件:用app.use定义,按先后顺序去执行
8.局部生效的中间件
不使用app.use的中间件就是局部生效的中间件
const zzj=function(req,res,next){
req.time=Date.now()
console.log('zzj',req.time)
next()
}
app.get('/',zzj,(req,res)=>{
console.log('走到了终点',req.time)
})
定义一个函数并在路由的中间的参数中去使用
zzj这个中间件就不会再影响到app.get('/user',、、、这个路由
9.定义多个局部中间件
在路由中,可以使用下面这两种等价的方式实现局部中间件
app.get('/',zzj1,zzj2,(req,res)=>{
console.log('走到了终点',req.time)
})
app.get('/',[zzj1,zzj2],(req,res)=>{
console.log('走到了终点',req.time)
})
注意:要在创建路由之前注册中间件(从上到下执行,先写路由的话就直接响应了)
中间件不要忘记next(),否则不往下走
next()的后面就不要再写代码了,防止逻辑混乱
10.中间件的分类
常见的中间件分类五大类:
- 应用级别的中间件
使用app.use / app.get / app.post绑定到app实力的中间件,叫应用级别的中间件。(全局和局部中间件都是应用级别的中间件)
- 路由级别的中间件
绑定到express.Router()实例上的中间件就是路由级别的中间件,router.use / router.get / router.post。
- 错误级别的中间件
专门用来捕获整个项目中发生的异常错误,防止项目异常崩溃,错误级别的中间件必须有四个行参(err,req,res,next)顺序不能改
app.get('/',(req,res)=>{
throw new Error('服务器内容错误')
res.send('hello')
})
app.use(function(err,req,res,next){
res.send('yichang',err)
})
错误之后直接去找错误中间件(跳过其他所有步骤,所以这里的路由可以走到下方的中间件),错误中间件是按有没有四个参数来识别的,即使写在所有路由后面也能捕获到错误。
然后我之前一直以为只有路由才能res.send,其实中间件也可以,他可以自己终止也能继续传递。(第一个函数里的hello就永远都不会输出)
注意:错误级别的中间件要定义在所有路由的最后面!(还是从上往下执行的)
- Express内置的中间件
Express 4.16.0版本开始内置了三个中间件
a. express.static 快速托管静态资源的内置中间件,例如html、css(无兼容性)、图片
b. express.json 解析JSON格式的请求体数据(有兼容性,4.16.0+版本可用)
c. express.urlencoded 解析URL-encoded格式的请求体数据(有兼容性,4.16.0+版本可用)
(有兼容性和无兼容性说的是这个功能对版本号有没有要求)
//解析表单中json格式的数据,解析完之后挂载到req上(raw中的json请求方式)
app.use(express.json())
//x-www-form-urlencoded
app.use(express.urlencoded({extended:false}))
extended作用:(不传的话express 4.x默认false,5.x默认true)



req.body可以来接收请求体发来的数据(但是如果没有配置解析表单数据的中间件的话,json数据发过来直接输出就是undefined,x-www-form-urlencoded发过来就是{})
- 第三方的中间件
在express 4.16.0之前经常使用body-parser这个第三方中间件来解析请求体数据
npm install body-parser
使用require导入并用app.use使用
const parser=require('body-parser')
app.use(parser.urlencoded({extended:false}))
注意:express.urlencoded这个内置中间件就是基于body-parser进一步封装出来的