一、中间件
1.1 简介
中间件(Middleware),特指业务流程的中间处理环节。我们可以把中间件比作工厂中的车间。比如:在处理铁矿石的时候,一般都要经过三个处理环节,从而保证处理过后的矿石达到标准的钢材。处理铁矿石的这三个中间处理环节,就可以叫做中间件。而中间件其实是路由的升级,也能达到请求的匹配,只不过必须要进行下一步处理,以到达最终的路由匹配,就像在工厂中生产产品,最后必须要出厂。
1.2 中间件的好处
1.3 中间件执行流程
当一个请求到达Express的服务器之后,可以连续调用多个中间件,从而对这次请求进行预处理。但是必须要有一个最终的匹配路由进行响应给客户端结果。
什么是中间件?【重点】
中间件就是在接收请求之后返回响应之前要执行的函数,而这些函数有一定的业务处理能力。
1.4 使用中间件【重点】
1.4.1 使用中间件的语法
app.use( [前缀,]中间件函数 ) router.use( [前缀,]中间件函数);
使用中间件要注意的事项:【重点】
1)、使用中间件的代码通常放在所有路由最前面;
2)、当中间件中的业务代码执行完成后要调用next()方法,next()方法会查找并执行后面能匹配上的路由;
3)、如果自定义中间件中业务代码发生异常时可以把异常信息当作参数传给next()方法,则next()方法默认会把异常信息直接输出界面上,但如果我们定义了逻辑错误处理中间件(也就是定义了含有err,req,res,next四个形参的中间件)则next()方法会把异常信息传给逻辑错误处理中间件中的err,并执行逻辑错误处理中间件;
1.4.2 中间件基本使用
用法一:使用app对象中提供的方法来设计路由则使用app.use()来使用自定义的中间件:
在项目根目录中安装第三方time-stamp包: npm i time-stamp
const express = require('express'); const timestamp = require('time-stamp'); const app = express(); app.listen(3000, () => { console.log('3000端口'); }); //自定义中间件(函数):获取当前日期时间 function getCurtimes(req, res, next) { // console.log(req, 111); // console.log(res, 222); // console.log(next, 333); let curTime = timestamp('YYYY-MM-DD HH:mm:ss'); req.Times = curTime; next(); //查询并执行后面能匹配上的路由 } //使用中间件: app.use(getCurtimes); app.get('/teacher', (req, res) => { res.send(`老师界面:${req.Times}`); }); //使用中间件: // app.use(getCurtimes); //注意: 使用中间件的代码通常放在所有路由最前面 app.get('/student', (req, res) => { res.send(`这是学生界面:${req.Times} `); });
用法二:使用模块化路由来设计路由则使用app.use()来使用自定义的中间件:router.use()
自定义模块化路由文件users.js的代码如下:
const express = require('express'); const router = express(); function test(req, res, next) { console.log('hello...'); next(); } //使用自定义的中间件: router.use(test); router.get('/zhuce', (req, res) => { res.send('处理注册'); }); router.get('/denglu', (req, res) => { res.send('处理登录'); }); module.exports = router;
1.4.3 中间件加前缀
用法一:
const express = require('express'); const timestamp = require('time-stamp'); const fs = require('fs'); const path = require('path'); const app = express(); app.listen(3000, () => { console.log('3000端口'); }); //自定义中间件(函数):获取当前日期时间 function getCurtimes(req, res, next) { // console.log(req, 111); // console.log(res, 222); // console.log(next, 333); let curTime = timestamp('YYYY-MM-DD HH:mm:ss'); // console.log(curTime, 888); req.Times = curTime; // next(); //查询并执行后面能匹配上的路由 if (1) { //中间件的业务没有异常 //if (0) { //中间件的业务没有异常 next(); } else { //中间件的业务发生异常 next('这是错误信息...'); } } //使用中间件: // app.use(getCurtimes); app.use('/student', getCurtimes); //注意: 自定义中间件只对/student路由生效,而/teacher路由不生效 app.get('/teacher', (req, res) => { res.send(`老师界面:${req.Times}`); }); //使用中间件: // app.use(getCurtimes); //注意: 使用中间件的代码通常放在所有路由最前面 app.get('/student', (req, res) => { res.send(`这是学生界面:${req.Times} `); });
用法二:
const express = require('express'); const app = express(); app.listen(3000, () => { console.log('3000端口'); }); let userRouter = require('./users.js'); // app.use(userRouter); app.use('/api', userRouter); //注意:在访问users.js模块化路由中的所有路由时路径都是以/api开头
模块化路由文件users.js的代码如下:
const express = require('express'); const router = express(); function test(req, res, next) { console.log('hello...'); next(); } //使用自定义的中间件: router.use(test); router.get('/zhuce', (req, res) => { res.send('处理注册'); }); router.get('/denglu', (req, res) => { res.send('处理登录'); }); module.exports = router;
1.5 中间件种类
1.5.1 应用级别的中间件
用户自定义的中间件用来处理具体的业务:应用级别的中间件
const express = require('express'); const timestamp = require('time-stamp'); const fs = require('fs'); const path = require('path'); const app = express(); app.listen(3000, () => { console.log('3000端口'); }); //自定义中间件(函数):获取当前日期时间 function getCurtimes(req, res, next) { // console.log(req, 111); // console.log(res, 222); // console.log(next, 333); let curTime = timestamp('YYYY-MM-DD HH:mm:ss'); // console.log(curTime, 888); req.Times = curTime; next(); //查询并执行后面能匹配上的路由 } //使用中间件: app.use(getCurtimes); app.get('/teacher', (req, res) => { res.send(`老师界面:${req.Times}`); }); //使用中间件: // app.use(getCurtimes); //注意: 使用中间件的代码通常放在所有路由最前面 app.get('/student', (req, res) => { res.send(`这是学生界面:${req.Times} `); });
1.5.2 错误中间件
1.5.2.1 404
用法一:在app对象中提供的方法中使用router.use()
const express = require('express'); const timestamp = require('time-stamp'); const fs = require('fs'); const path = require('path'); const app = express(); app.listen(3000, () => { console.log('3000端口'); }); //自定义中间件(函数):获取当前日期时间 function getCurtimes(req, res, next) { // console.log(req, 111); // console.log(res, 222); // console.log(next, 333); let curTime = timestamp('YYYY-MM-DD HH:mm:ss'); // console.log(curTime, 888); req.Times = curTime; next(); //查询并执行后面能匹配上的路由 } //使用中间件: // app.use(getCurtimes); app.use('/student', getCurtimes); //自定义中间件只对/student路由生效,而/teacher路由不生效 app.get('/teacher', (req, res) => { res.send(`老师界面:${req.Times}`); }); //使用中间件: // app.use(getCurtimes); //注意: 使用中间件的代码通常放在所有路由最前面 app.get('/student', (req, res) => { res.send(`这是学生界面:${req.Times} `); }); //404错误处理中间件: app.use((req, res, next) => { res.send('这是404错误'); // res.sendFile(path.join(__dirname, '404.html')); });
用法二:在模块化路由中使用router.use()
const express = require('express'); const router = express(); function test(req, res, next) { console.log('hello...'); next(); } //使用自定义的中间件: router.use(test); router.get('/zhuce', (req, res) => { res.send('处理注册'); }); router.get('/denglu', (req, res) => { res.send('处理登录'); }); //404错误处理中间件: router.use(function(req, res, next) { res.send('404错误'); }); module.exports = router;
1.5.2.2 逻辑错误处理中间件:
const express = require('express'); const timestamp = require('time-stamp'); const fs = require('fs'); const path = require('path'); const app = express(); app.listen(3000, () => { console.log('3000端口'); }); //自定义中间件(函数):获取当前日期时间 function getCurtimes(req, res, next) { // console.log(req, 111); // console.log(res, 222); // console.log(next, 333); let curTime = timestamp('YYYY-MM-DD HH:mm:ss'); // console.log(curTime, 888); req.Times = curTime; // next(); //查询并执行后面能匹配上的路由 // if (1) { //中间件的业务没有异常 if (0) { //中间件的业务没有异常 next(); } else { //中间件的业务发生异常 next('这是错误信息...'); } } //使用中间件: app.use(getCurtimes); app.get('/teacher', (req, res) => { res.send(`老师界面:${req.Times}`); }); //使用中间件: // app.use(getCurtimes); //注意: 使用中间件的代码通常放在所有路由最前面 app.get('/student', (req, res) => { res.send(`这是学生界面:${req.Times} `); }); //逻辑错误处理中间件 function proError(err, req, res, next) { //将错误信息写入日志文件: fs.writeFileSync(path.join(__dirname, 'err.log'), err); res.send('出错了!!!'); } app.use(proError);
1.5.3 内置中间件
1.5.3.1 开放静态资源:express.static() 【重点】
中文文档参考地址: 利用 Express 托管静态文件 - Express 中文文档 | Express 中文网
什么是开放静态资源?
就是让用户通过服务器来访问我们的静态资源(比如:.js文件、.css文件、图片文件、.html文件)。
开放静态资源的用法:
app.use(express.static(绝对路径))
注意:将某个目录开放之后再使用时不能带上该目录
const express = require('express'); const path = require('path'); const app = express(); app.listen(3000, () => { console.log('3000端口'); }); //express的开放静态资源: //注意:将某个目录开放之后再使用时不能带上该目录 // app.use('/image', express.static(path.join(__dirname, 'image'))); // app.use('/js', express.static(path.join(__dirname, 'js'))); app.use(express.static(path.join(__dirname, 'public'))); let userRouter = require('./3-users.js'); app.use(userRouter); //app.use('/api', userRouter); //注意:在访问3-users.js模块化路由中的所有路由时路径都是以/api开头
1.5.3.2 接收post参数【重点】
以post方式发送参数的格式有两种:键值对、json对象
接收post的键值对格式发送的参数:
第一步:创建以.http结尾的文件,在这个.http文件中模块以post方式的键值对格式发送参数,代码如下:
POST http://localhost:4000/login HTTP/1.1 Content-Type:application/x-www-form-urlencoded uname=lisi&pwd=123&email=lisi@qq.com
第二步:在express中使用内置中间件express.urlencoded()来接收post方式的键值对格式发送过来的参数,代码如下:
app.use(express.urlencoded({extended:false}))
中文文档参考地址: Express 4.x - API Reference - Express 中文文档 | Express 中文网
const express = require('express'); const app = express(); app.listen(4000, () => { console.log('4000端口'); }); //使用express.urlencoded()内置中间件接收post的键值对格式发送的参数: app.use(express.urlencoded({ extended: false })); //接收post方式发送的参数: app.post('/login', (req, res) => { console.log(req.body); res.send(`post请求方式 : ${JSON.stringify(req.body)}`); });
接收post的json对象格式发送的参数:
第一步:创建以.http结尾的文件,在这个.http文件中模块以post方式的json对象格式发送参数,代码如下:
POST http://localhost:4000/login HTTP/1.1 Content-Type:application/x-www-form-urlencoded uname=lisi&pwd=123&email=lisi@qq.com #### #post方式的json对象格式的参数 POST http://localhost:4000/demo HTTP/1.1 Content-Type: application/json {"bkname":"web开发","price":88}
第二步:在express中使用内置中间件express.json()来接收post方式的json对象格式发送过来的参数,代码如下:
app.use(express.json())
中文文档参考地址:Express 4.x - API Reference - Express 中文文档 | Express 中文网
const express = require('express'); const app = express(); app.listen(4000, () => { console.log('4000端口'); }); //使用express.urlencoded()内置中间件接收post的键值对格式发送的参数: app.use(express.urlencoded({ extended: false })); //使用express.json()内置中间件接收psot的json对象格式发送的参数: app.use(express.json()); //接收post方式键值对格式发送的参数: app.post('/login', (req, res) => { let { uname, pwd, email } = req.body; console.log(req.body, uname, pwd, email); res.send(`post请求方式 : ${JSON.stringify(req.body)}`); }); //接收post方式json对象格式发送的参数: app.post('/demo', (req, res) => { let { bkname, price } = req.body; res.send(`post的json对象格式的参数:${JSON.stringify(req.body)}`); });
1.5.4 第三方中间件
1.5.4.1 小图标
serve-favicon
1.5.4.2 图片验证码
第三方svg-captcha包用来动态生成图片验证码,代码如下:
const express = require('express'); var svgCaptcha = require('svg-captcha'); const app = express(); app.listen(5000, () => { console.log('5000端口'); }); //创建图片验证码 app.get('/codes', (req, res) => { let svgObj = svgCaptcha.create({ size: 6, ignoreChars: '0o1i', noise: 3, color: true, background: '#cc9966' }); console.log(svgObj); // res.setHeader('content-type', 'image/svg+xml'); res.type('svg'); res.end(svgObj.data); });