002中间件机制
[A] 中间件简介
1. 中间件就是express框架定义的一堆方法,可以接收客户端发来的请求,对请求做出响应
2. 是对于同一个请求,允许我们设置多个中间件,这些中间件会按照定义的顺序依次往下对请求进行处理
[B] 中间件的组成
中间件由两部分组成:
1. 中间件方法 —— 由express框架提供
2. 请求处理函数 —— 由开发人员提供
示例:
app.get('请求路径', '处理函数') // 接收并处理get请求 app.post('请求路径', '处理函数') // 接收并处理post请求
【注】这里,express框架提供中间件方法get和post,开发人员提供 '处理函数'
[C] 中间件的执行过程
对于同一个请求,设置多个中间件对其进行处理时
1. 默认情况下,请求从上到下依次匹配中间件,一旦匹配成功,则终止匹配
2. 可以调用next方法将请求的控制权交给下一个中间件,直到遇到结束请求(即没有调用next方法的中间件)的中间件为止
如:
// 中间件1
app.get('./index', (req, res, next) => { res.name = 'Carrey' next() })
// 中间件2
app.get('/index', (req, res) => {
res.send(req.name)
})
// 1. 默认情况下,当客户端访问/index路径时,请求会从上往下首先匹配到中间件1,然后结束匹配
// 2. 由于检测到中间件1将请求的控制权往下移交了,因此会继续往下进行请求处理。
【注】:
1. 前面讲到app.send()方法是一个高度封装的方法,调用一次send,相当于调用了:
res.setHeader(), res.writeHead(), res.write(), res.end()等一系列完整发送一次数据的诸多方法
2. 由于一次请求中,调用write或者end方法发送数据后,就不得再调用setHead去设置请求头了
因此,用多个中间件处理同一个请求时,所有中间件最多只能调用一次send方法,多次调用会报错.
[D] 中间件的使用
// app.get用于处理get请求 app.get('/index', (req, res) => { res.send('您访问的是index页面~') })
// app.post用于处理post请求 app.get('/me', (req, res) => { res.send('您访问的是me页面~') })
app.use((req, res, next) => {
console.log(req.url)
next()
})
[E] 中间件的应用
1. 路由保护
客户端在访问需要登陆的页面时,可以首先使用中间件判断用户登陆状态, 若未登录则拦截请求,并直接响应,禁止用户进入对应需要登陆的页面
app.use('/admin', (req, res, next) => { if(未登录){ res.send('您尚未登陆,请登陆后访问~') } else { next() } })
2. 网站维护公告
当网站在维护时,或者暂时不开放时, 可以对所有请求进行拦截,并相应正在维护中..
app.use((req, res, next) => { req.send('当前网站正在维护中...') })
3. 定义404页面
当之前的路由全部没有匹配上, 则说明用户访问的网址不存在,此时需要返回404页面, 即需要在所有中间件的最后,添加一个404中间件
app.use((req, res, next) => { res.send('404 您所访问的页面不存在~') })
// 此时, 响应码仍为200, 这是send方法自动添加的, 此时我们需要手动更改状态码为404,处理函数中需添加: res.status(404)
即上述代码更改为:
app.use((req, res, next) => { res.status(404) res.send('404 您所访问的页面不存在~') // 或者链式掉用 res.status(404).send('404 您所访问的页面不存在~') })
4. 处理错误中间件
1. 在程序执行过程中, 可能会出现无法预料的错误, 如:读取文件失败, 数据库连接不成功等
此时需要定义一个中间件集中去处理各种错误
app.use((err, req, res, next) => { res.status(500).send('服务器发生了错误~') })
2. 注意事项
1. 错误处理中间件的回调函数中共有4个参数,即拥有四个处理函数的中间件即为错误处理中间件
2. 回调函数的第一个参数为err,程序中所有的报错信息都会被err捕获。
3. 上述方法定义的错误处理中间件只能主动捕获同步代码中发生的错误,
无法捕获异步代码中发生的错误,此时,我们需要将错误通过next()方法传递出去
当异步代码出错时,调用next()方法,并且将错误信息通过参数形式传递给next()方法,即可出发错误处理中间件
即:
if(err){ next(err) // 此时这个err会被直接传入错误处理中间件 }
3. 此外,还可以使用try-catch语法进行错误捕获或传递,防止后台报错后停止运行。
但try-catch语法只能捕获同步代码,异步函数的错误,无法捕获其他类型错误,如回调函数,promise对象
[F] 构建模块化路由
典型示例:
const express = require('express') const app = express() // 创建路由 const home = express.Router() // 将路由与请求路径匹配 app.use('/home', home) // 在home路由下继续创建路由处理函数 home.get('/index', () => { res.send('欢迎来到首页') })
1. 基础路由的实现过程
1. 一级路由匹配请求路径
app.use('/home', home)
2. 二级路由创建处理函数
home.get('/index', () => { res.send('欢迎来到首页') })
2. 构建模块化路由
实现过程:
1. 将二级路由通过模块化进行封装
// home.js const home = express.Router() home.get('/index', () => { res.send('欢迎来到首页') })
module.exports = home
2. 在主文件中,通过模块化导入后使用
const home = require('./home.js')
app.use('/home', home)
[G] 请求参数的获取
1. GET请求参数的获取
express框架中使用req.query对象获取GET参数,框架内部已经将GET参数转换为对象并返回
示例:
// 现要获取GET请求中地址栏?后面的参数 // 若:http://localhost:3000/?name=jack&age=18 app.get('/', (req, res) => { console.log(req.query) // 返回值为一个对象{"name": "jack", "age": "18"} })
2. POST请求参数的获取
express中接受post请求参数需要借助第三方包: body-parser
示例:
// 引入body-parser模块 const bodyParser = require('body-parser') // 配置body-parser模块 app.use(bodyParser.urlencoded({ extended: false })) // 接收请求 app.post('/add', (req, res) => { // 接收请求参数 console.log(req.body) })
1. app.use对所有请求进行拦截,并且用bodyParser.urlencoded()方法检查请求中是否含有参数。
若有则将请求参数转换为对象并保存到req.body属性中
2. bodyParser.urlencoded()方法中传入了参数extended
extended: true —— 表示方法内部会使用第三方模块qs对参数格式进行处理
extended: false —— 表示方法内部会使用queryString这个系统模块对参数格式进行处理
两者功能上都能将参数转换成对象,相比之下qs功能更强大一些,但这里系统模块已经足够了,这也是官方推荐的写法
3. 然后,在方法内部调用了next()方法,将请求控制权往下传递。
[H] express路由参数
get请求参数的获取
app.get('/index/:id/:name', () => {
console.log(req.params) // 返回对象 { id: xxx, name: xxx }
})
在客户端发送请求时,必须穿入id和name两个参数,否则查找不到这个index页面
即 localhost:3000/index/123/jack 才可正确访问