中间件-理解
对express
而言,中间件
是它的一个非常重要的概念,掌握中间件的思想对于理解学习express,提升编程水平都有很大的帮助。
生活中的中间件
一道复杂的任务拆分成几个小步骤:
- 步骤1
- 步骤2
- 步骤3
我们可以称其中的每一个处理环节就是一个中间件。
招程序员的流程
- 面试
- hr
- 技术
- 综合
- 背调(背景调查),委托第三方的公司去做背调。
- 发offer
- 体检
- 入职-签合同
- 转正
- 离职
express中间件
中间件
是一个特殊的url地址处理函数
- 中间件是 express 的最大特色,也是最重要的一个设计。Express是一个自身功能极简,完全是
路由
和中间件
构成一个web开发框架:从本质上来说,一个Express应用就是在调用各种中间件。 - 一个 express 应用,就是由许许多多的中间件来完成的
作用
- 执行任何代码。
- 修改请求和响应对象。
- 终结请求-响应循环(结束请求)。
- 调用堆栈中的下一个中间件
分类
- 应用级中间件
app.use(express.static('public'))
- 路由级中间件
app.get('请求路径',(req,res,next)=>{})
- 错误处理中间件
- 内置中间件
app.use(express.static('public'))
- 第三方中间件
const multer = require('multer')
app.post("/postfile",upload.single('cover'), function(req,res){
// req.file 记录了文件上传的信息
// req.body 记录了其它普通参数(非文件)的信息
// 其它操作
res.send({msg: 'pk'})
})
中间件-格式及基本示例
中间件本质就是一个函数,它被当作 app.use(中间件函数)
的参数来使用,或者是某个路由处理函数中使用。
1.定义格式
// 具名函数格式:
const handler1 = (req, res, next) => {
console.log(Date.now());
next();
}
app.use(handler1);
// 匿名函数格式:
app.use((req, res, next) => {
console.log(Date.now());
next();
});
说明: 中间件函数中有三个基本参数,req
、res
、next
- req就是请求相关的对象,它和下一个中间件函数中的req对象是一个对象
- res就是响应相关的对象,它和下一个中间件函数中的res对象是一个对象
- next:它是一个函数,调用它将会跳出当前的中间件函数,执行后续中间件;如果不调用next,也不执行res.end,则整个请求都会在当前中间件卡住。
2.示例
const express = require('express')
const app = express();
app.use((req, res, next) => {
console.log("第1个中间件");
req.a1 = 100;
next();
});
app.use((req, res, next) => {
console.log("第2个中间件");
res.setHeader('content-type', 'text/html;charset=utf8');
res.a2 = 200;
next();
});
app.use((req, res, next) => {
console.log("第3个中间件");
req.a3 = 300;
console.log(req.a1,req.a2)
res.end('中间件');
});
app.listen(3000,()=>{
console.log('express应用在3000端口启动了');
})
- 多个中间件先后顺序。
- 注意通过req来附加额外的信息。
- 如果不用next(),则不会进入下一个中间件。
3.执行流程
中间件-四种匹配规则
- a
pp.use(中间件)
是应用级中间件,所有的请求都能匹配。 app.use('/apiname',中间件)
。匹配请求路径是/apiname的请求。app.get('/apiname',中间件)
。匹配get类型并且请求路径是/apiname的请求,就是我们前面说的路由。app.get('/apiname',中间件1,中间件2)
。一个路由中使用多个中间件。
中间件的应用-访问日志
模拟日志功能:你是后端程序员,你写代码去记录所有用户在哪个时间点访问了哪个页面?
function getClientIp(req) {
return req.headers['x-forwarded-for'] ||
req.connection.remoteAddress ||
req.socket.remoteAddress ||
req.connection.socket.remoteAddress;
}
app.use((req,res,next) => {
console.log('我来用记录访问日志')
// 获取当前用户访问的页面地址: req.url
// 获取当前用户的id的地址: ?
// 当前时间
const dt = new Date()
console.log(dt.toLocaleTimeString())
console.log(req.url)
console.log('来自:', getClientIp(req));
// fs写入某个日志文件中
next();
})
路由级中间件
使用场景
接口数量较多时,代码不好管理。以大事件的代码为例,我们定义了管理员角色的接口和普通游客的接口,这些接口如果全写在一个入口文件中(如下只是显示了4个接口,如果是40个接口,就会很难读了),也不好维护的。
const express = require('express');
const app = express();
// 两种用户的操作,对应不同的接口
app.get('/getfrontdetail', (req, res) => {
res.send('获取游客详情');
});
app.get('/getfrontinfo', (req, res) => {
res.send('获取游客信息');
});
// 两种用户的操作,对应不同的接口
app.get('/getadmincate', (req, res) => {
res.send('管理员获文章类别信息');
});
app.get('/getadmininfo', (req, res) => {
res.send('获取管理员信息');
});
app.listen(3000, () => {
console.log(3000);
});
我们的目标就是把它们拆开到不同的文件中,以便于管理。
思路
第一步 整理 分类
整理接口名。对众多的接口名进行整理和分类,以一级目录,二级目录这样的方式进行。例如:
/admin/getcate
/admin/getinfo
/front/getinfo
/front/getdetail
第二步 分模块 定义 导出
通过nodejs的模块化,分模块定义路由中间件,并导出
第三步 主入口文件 导入使用
在主文件中,导入并使用路由中间件。
步骤
第一步:定义两个模块(两个js文件)。分别保存不同的分类的接口。
- front.js
- server.js
// ./router/front.js
const express = require('express');
const router = express.Router();
router.get('/getinfo', function(req, res) {
res.send('getinfo');
});
router.get('/getdetail', function(req, res) {
res.send('getdetail');
});
module.exports = router;
// ./router/server.js
const express = require('express');
const router = express.Router();
router.get('/getinfo', function(req, res) {
res.send('管理员getinfo');
});
router.get('/getdetail', function(req, res) {
res.send('管理员getdetail');
});
module.exports = router;
注意: 上面epxress.Router()
的用法。
第二步:在主入口文件中使用它们
const express = require('express');
const app = express();
const frontRouter = require('./router/front');
const serverRouter = require('./router/admin');
app.use('/front', frontRouter);
app.use('/server', serverRouter);