一、中间件

1.1 简介

中间件(Middleware),特指业务流程的中间处理环节。我们可以把中间件比作工厂中的车间。比如:在处理铁矿石的时候,一般都要经过三个处理环节,从而保证处理过后的矿石达到标准的钢材。处理铁矿石的这三个中间处理环节,就可以叫做中间件。而中间件其实是路由的升级,也能达到请求的匹配,只不过必须要进行下一步处理,以到达最终的路由匹配,就像在工厂中生产产品,最后必须要出厂。

宝兰德中间件 spring boot 宝兰德中间件教程_自定义

1.2 中间件的好处

1.2.1、使用中间件可以拆分我们的业务,使业务流程更清晰。

1.2.2、后期维护更加方便。

1.3 中间件执行流程

什么是中间件?【重点】

当一个请求到达Express的服务器之后,可以连续调用多个中间件,从而对这次请求进行预处理。但是必须要有一个最终的匹配路由进行响应给客户端结果。

宝兰德中间件 spring boot 宝兰德中间件教程_自定义_02

1.4 使用中间件

1.4.1 使用中间件的语法

app.use( [前缀,]中间件函数 )
callback( req,res,next )

//在模块化路由文件中使用自定义的中间件:
router.use(中间件函数)

使用中间件要注意的事项:【重点】

1)、在自定义的中间件函数内部要手动调用next()方法,next()方法会执行后续操作;

2)、使用中间件的代码通常放在所有路由的最前面,但404错误中间件通常放在所有路由的最后面;

3)、如果中间件中的业务出现异常时通常会把异常信息当作next()方法的参数,如果没有定义使用逻辑错误处理中间件(含有err,req,res,next四个形参的中间件),则next()会把这些异常信息直接输出界面上,如果定义并使用了逻辑错误处理中间件则next()方法会自动查找并执行这个逻辑错误处理中间件;

1.4.2 基本使用

const express = require('express');
const timestamp = require('time-stamp');
const app = express();
app.listen(4000, () => {
    console.log('4000端口');
});
//自定义中间件
function getTimes(req, res, next) {
    //获取当前日期时间并放在req对象上
    req.curtime = timestamp('YYYY-MM-DD HH:mm:ss');
     next(); //next()方法:查找并执行后面能匹配上的路由
}
//使用中间件:(必须在界面前使用)
app.use(getTimes);
//学生界面
app.get('/student', (req, res) => {
    res.send(`学生界面: ${req.curtime}`);
});
//老师界面
app.get('/teacher', (req, res) => {
    res.send(`老师界面:  ${req.curtime}`);
});

1.4.3 中间件加前缀

在使用自定义中间件时加前缀,则只对某个路由才使用这个自定义的中间件,代码如下

const express = require('express');
const timestamp = require('time-stamp');
const path = require('path');
const app = express();
app.listen(4000, () => {
    console.log('4000端口');
});
//自定义中间件
function getTimes(req, res, next) {
    //获取当前日期时间并放在req对象上
    req.curtime = timestamp('YYYY-MM-DD HH:mm:ss');
    if (1) { //正确
        // if (0) { //错误
        next();
    } else { //错误
        next('这里错误信息...');
    }
}
//使用中间件:
app.use(getTimes);
app.use('/teacher', getTimes);

//学生界面
app.get('/student', (req, res) => {
    res.send(`学生界面: ${req.curtime}`);
});

//老师界面
app.get('/teacher', (req, res) => {
    res.send(`老师界面:  ${req.curtime}`);
});

1.5 中间件种类

1.5.1 应用级别的中间件

const express = require('express');
const app = express();

app.listen(3000, () => {
    console.log('web服务器工作在3000端口');
});
const indexRouter = require('./index.js');
app.use(indexRouter);

index.js路由模块文件代码如下

const express = require('express');
const router = express.Router();
//设计路由:
//首页轮播图:
router.get('/banners', (req, res) => {
    res.send('轮播图');
});

//首页同步课程: 
router.get('/syncourse', (req, res) => {
    res.send('同步课程');
});

//首页精品课程:
router.post('/jpincourse', (req, res) => {
    res.send('精品课程');
});
//暴露router  一定记得暴露router
module.exports = router;

1.5.2 错误中间件

1.5.2.1 404错误中间件:

const express = require('express');
const timestamp = require('time-stamp');
const path = require('path');
const app = express();

app.listen(4000, () => {
    console.log('4000端口');
});

//自定义中间件
function getTimes(req, res, next) {
    //获取当前日期时间并放在req对象上
    req.curtime = timestamp('YYYY-MM-DD HH:mm:ss');
    if (1) { //正确
        // if (0) { //错误
        next();
    } else { //错误
        next('这里错误信息...');
    }
}

//使用中间件:
app.use(getTimes);

//学生界面
app.get('/student', (req, res) => {
    res.send(`学生界面: ${req.curtime}`);
});

//老师界面
app.get('/teacher', (req, res) => {
    res.send(`老师界面:  ${req.curtime}`);
});

//404错误处理中间件:
// app.use(function(req, res, next) {
//     res.send(`404错误!`);
// });

function err404(req, res, next) {
    //res.send(`这是404错误!!`);
    res.sendFile(path.join(__dirname, '404.html'));
}
app.use(err404);

//自定义中间件:逻辑错误处理中间件
function proError(err, req, res, next) {
    console.log(err, 8888);
    //将错误信息写入错误日志文件中:
    // fs.writeFileSync('system.log',err);
    res.send(err);
}
app.use(proError);

1.5.2.2 逻辑错误中间件:

const express = require('express');
const timestamp = require('time-stamp');
const app = express();

app.listen(4000, () => {
    console.log('4000端口');
});
//自定义中间件
function getTimes(req, res, next) {
    //获取当前日期时间并放在req对象上
    req.curtime = timestamp('YYYY-MM-DD HH:mm:ss');
    // if (1) { //正确
    if (0) { //错误
        next();
    } else { //错误
        next('这里错误信息...');
    }
}

//使用中间件:
app.use(getTimes);
//学生界面
app.get('/student', (req, res) => {
    res.send(`学生界面: ${req.curtime}`);
});
//使用中间件:
// app.use(getTimes);
//老师界面
app.get('/teacher', (req, res) => {
    res.send(`老师界面:  ${req.curtime}`);
});
//自定义中间件:逻辑错误处理中间件
function proError(err, req, res, next) {
    console.log(err, 8888);
    //将错误信息写入错误日志文件中:
    // fs.writeFileSync('system.log',err);
    res.send(err);
}
app.use(proError);

1.5.3 内置中间件

1.5.3.1 开放静态资源:express.static()【重点】

就是让用户通过服务端来访问我们的静态资源(比如:.js文件、.css文件、图片文件、html文件)。

注意:将某个目录开放之后在访问时不需要带上该目录

入口文件的代码如下

const express = require('express');
const timestamp = require('time-stamp');
const path = require('path');
const app = express();

app.listen(5000, () => {
    console.log('5000端口');
});

//使用内置中间件express.static()来开放静态资源
//注意:将某个目录开放之后在访问时不需要带上该目录
// app.use(express.static(path.join(__dirname, 'html/image')));
// app.use('/image', express.static(path.join(__dirname, 'html/image')));
// app.use('/js', express.static(path.join(__dirname, 'html/js')));
// app.use('/css', express.static(path.join(__dirname, 'html/css')));
app.use(express.static(path.join(__dirname, 'public')));

//引入自定义的模块化路由文件:
let userRouter = require('./user.js');
app.use(userRouter);

user.js路由模块文件代码如下

const express = require('express');
const router = express.Router();
const path = require('path');
const fs = require('fs');

//设计路由

//显示登录界面:
router.get('/login', (req, res) => {

    res.sendFile(path.join(__dirname, 'login.html'));
});

// router.get('/image/banner3.png', (req, res) => {

//     let content = fs.readFileSync(path.join(__dirname, 'html/image/banner3.png'));
//     res.send(content);
// });

module.exports = router;

login.html文件代码如下

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="js/common.js"></script>
</head>

<body>
    <div><img src="./image/banner3.png" alt=""></div>
    <!-- <div><img src="./banner3.png" alt=""></div> -->
    <div>帐号:<input type="text" name="user"></div>
    <div>密码:<input type="password" name="pwd"></div>
    <div><input type="submit" value="登录"></div>
</body>

</html>

1.5.3.2 接收post参数【重点】

使用内置中间件express.urlencoded()来接收post方式的键值对格式发送的参数:

参考手册地址: Express 4.x - API Reference - Express 中文文档 | Express 中文网

app.use(express.urlencoded({extended:false}))

使用内置中间件express.json()来接收psot方式的json格式发送的参数:

app.use(express.json())

通过vscode插件rest client来模拟发送post方式的请求并传参:【重点】

api.http文件参考如下:

@url=http://localhost:5000

get {{url}}/city?cid=111&usr=lisi HTTP/1.1

###
POST {{url}}/denglu HTTP/1.1
Content-Type: application/x-www-form-urlencoded

user=zhangsan&age=20&email=zs@qq.com


###
POST {{url}}/myjson HTTP/1.1
Content-Type: application/json

{"xingming":"李四","tel":110,"age":20}

1.5.4 第三方中间件

1.5.4.1 小图标

serve-favicon:这个第三方中间件用来动态生成网站小图标(favicon.ico)

1.5.4.2 图片验证码

svg-captcha

//引入express  
const express = require('express');
const fs = require('fs');
const path = require('path');
const svgcaptcha = require('svg-captcha');
// console.log(express);
//创建应用
const app = express();
// console.log(app);

//监听端口:app.listen()
app.listen(3000, () => {
    console.log('服务运行在3000端口');
});

//生成图片验证码:
app.get('/getcode', (req, res) => {
    let svg = svgcaptcha.create({ 
                                   width: 100, 
                                   height: 80, 
                                   background: '#cc9966', 
                                   size: 6, 
                                   ignoreChars: '0o1i', 
                                   noise: 3, 
                                   color: true 
                              });
    console.log(svg);
    res.send(svg.data);
});