目录
路由
解释
在 Express 中,路由(Route)用于定义应用程序的端点(URIs)以及如何响应客户端请求。每个端点由一个或多个处理函数(中间件函数)组成,这些函数在匹配的路由上执行。
路由是 Express 应用中一个非常重要的概念,用于组织应用的不同部分,并映射不同的 URL 到相应的处理逻辑。
使用方式
javascript
app.use('/api/vi/tours', )
app.use('/api/v1/tours', tourRouter);
app.use('/api/v1/users', userRouter);
app.use('/api/v1/reviews', reviewRouter);
app.all('*', (req, res, next) => {
next(new AppError(`Can't find ${req.originalUrl} on this server!`, 404));
});
/api/v1/tours、/api/v1/users、/api/v1/reviews ,* 表示任意路由路径
userRouter、tourRouter、reviewRouter表示将子路由注册为中间件
javascript
// tourRoutes.js
// tourRouter
const router = express.Router();
router.use('/:tourId/reviews', reviewRouter);
router
.route('/top-5-cheap')
.get(tourController.aliasTopTours, tourController.getAllTours);
module.exports = router;
中间件
解释
在 Express 框架中,中间件(middleware)是一个函数,它具有访问请求对象(req)、响应对象(res),以及应用程序中下一个中间件函数的能力。中间件函数可以执行一系列操作,例如修改请求和响应对象、结束请求-响应周期,或者将控制权传递给下一个中间件。
使用方式
javascript
app.use([path,] callback [, callback...])
app.all([path,] callback [, callback...])
中间件执行流程
中间件类型
express会根据参数数量以及类型判断属于什么中间件类型
内置中间件
app.use(express.json()) // 解析request body
app.use(express.static(`${__dirname}/public`)); // 解析静态资源
...
应用中间件
app.use((req, res, next) => {
next()
}); // 三个参数
错误处理中间件
app.use((err, req, res, next) => {
return res.status(errcode).json({
status: 'error',
});
}); // 错误处理中间件需要传入4个参数
第三方中间件
const xss = require('xss-clean'); //防止跨站脚本攻击(XSS
const hpp = require('hpp'); //防止 HTTP 参数污染攻击。
const cors = require('cors'); //用于处理跨域请求
const rateLimit = require('express-rate-limit'); //限制请求速率,防止滥用或恶意攻击
const helmet = require('helmet'); //设置各种 HTTP 头来抵御一些常见的 web 安全问题
const morgan = require('morgan'); //记录 HTTP 请求日志
...
app.use(xss())
...
路由中间件
// routes/userRoutes 文件夹
const router = express.Router(); //使用 express.Router() 注册子路由
router.xxx // router的用法和app完全相同, 具体代码如下
...
// app.js 文件
const userRouter = require('./routes/userRoutes');
app.use('/api/v1/tours', userRouter); 使用app.use注册子路由
...
好了,前面介绍那么多,这只是一个大概的介绍,光看大概是很懵的,最重要的还是直接上代码,查看控制台输出内容,能够更好的理解中间件执行流程。
路由注册和中间件注册
代码
javascript
// app.js
const express = require('express');
const morgan = require('morgan');
const childRouter = require('./childRoutes');
const app = express();
// app.use(middleware)注册全局中间使用app.use
app.use(morgan('dev')); // 第三方中间件,用于在控制台打印请求日志, 比如:DELETE /api5/2 200 12.226 ms - 22
app.use(express.json()) // 内置中间件,解析request body
app.use((req, res, next) => { // 自定义全局中间件
req.query.a = 1
console.log('全局路由中间件1')
next()
});
app.use((req, res, next) => { // 同上
console.log(req.query.a) // 1
console.log('全局路由中间件2')
next()
});
app.use((req, res, next) => { // app.use 注册中间件可以注册多个
console.log('全局路由中间件3')
next()
}, (req, res, next) => {
console.log('全局路由中间件4')
next()
});
app.use('/api1', (req, res) => { // '/api1'指定在什么请求路径上注册中间件,(可能需要在这个路径上做一些安全的校验等等)
console.log('全局路由中间件5')
return res.status(200).json({
status: 'hello'
});
});
app.use('/api2', (req, res, next) => { // 同样可以注册多个中间件
console.log('全局路由中间件5')
next()
}, (req, res, next) => {
console.log('全局路由中间件6')
return res.status(200).json({ // 代码顺序从上往下依次执行中间件函数,直到res响应http请求
status: 'hello'
});
});
app.get('/api3/:id', (req, res) => { // 为指定路径指定get、 post、delete、 patch方法
return res.status(200).json({
status: 'hello get'
});
});
app.delete('/api3/:id', (req, res) => {
return res.status(200).json({
status: 'hello delete'
});
});
// 同上简写
app
.route('/api4/:id')
.get((req, res) => { // 链式判断请求方法 get
return res.status(200).json({
status: 'hello get'
});
})
.delete((req, res) => { // delete
return res.status(200).json({
status: 'hello delete'
});
})
app.use('/api5', (req, res, next) => { // 在路径/api3上,注册中间件和子路由childRouter,子路由的用法和全局app上指定路径和注册中间件的语法完全一样
console.log(5)
next()
}, childRouter);
app.all('*', (req, res, next) => { // 如果路由都没命中执行此中间件 * 表示匹配所有路径
next(new Error(`Can't find ${req.originalUrl} on this server!`));
});
app.use((err, req, res, next) => {
return res.status(500).json({
status: 'error',
});
});
app.listen(3000, () => {
console.log(`App running on port ${3000}...`);
});
app全局路由接口请求以及代码解析
示例1
请求路由
控制台打印
解释:访问'/api1'路径,执行app.use注册的应用级中间件,最后命中到/api1路径,通过res响应请求。
代码执行流程
示例2
请求路由
控制台打印
解释:如上一样,请求'/api2', 执行app.use注册的应用级中间件,最后命中到/api1路径,通过res响应请求。
代码执行流程
示例3
请求路由
控制台打印
解释:执行过了上面代码注册的全局应用中间件,1 => 2 => 3 => 4, 然后命中api3/:id 这个路由路径(get请求)。
可以通过 app.[method] 注册路由,注册中间件,也可以app.route注册路由,然后链式 .get(middleware).delete(middleware) 为其注册指定请求方式。
代码执行流程
示例4
请求路由
控制台打印
解释:请求/api路径,正常执行完"1 => 2 => 3 => 4"中间件,但是到最后都没有命中这个路由,被app.all注册的路由和中间件捕获,app.all表示能够接受任意请求方式 post、get、delete、patch, " * " 表示任意路径, next一个错误处理, 将处理传递给错误处理中间件,然后res结束请求。
代码执行流程
中间件req继承
中间件的req会被下一个中间req所"继承",也就是上一个中间件为req设置的值能被下一个req访问
嵌套子路由
解释
Express 中的嵌套路由是指在一个路由处理器中定义另一个路由。这允许你组织和模块化路由的代码,将相关的路由分组在一起,以提高代码的可维护性和可读性。
代码
javascript
//childRouter
const express = require('express');
const router = express.Router();
router.use((req, res, next) => { // 自定义子路由中间件
console.log('子路由中间件1')
next()
});
router.use((req, res, next) => { // 同上
console.log('子路由中间件2')
next()
});
router.use((req, res, next) => { // router.use 注册中间件可以注册多个
console.log('子路由中间件3')
next()
}, (req, res, next) => {
console.log('子路由中间件4')
next()
});
router.use('/child-api1', (req, res) => { // '/api1'指定在什么请求路径上注册中间件,(可能需要在这个路径上做一些安全的校验等等)
console.log('子路由中间件5')
return res.status(200).json({
status: 'hello child route'
});
});
router.use('/child-api2', (req, res, next) => { // 同样可以注册多个中间件
console.log('子路由中间件5')
next()
}, (req, res, next) => {
console.log('子路由中间件6')
return res.status(200).json({ // 代码顺序从上往下依次执行中间件函数,直到res响应http请求
status: 'hello child route'
});
});
router.get('/child-api3/:id', (req, res) => { // 为指定路径指定get、 post、delete、 patch方法
return res.status(200).json({
status: 'hello get child route'
});
});
router.delete('/child-api3/:id', (req, res) => {
return res.status(200).json({
status: 'hello delete child route'
});
});
// 同上简写
router
.route('/child-api4/:id')
.get((req, res) => { // 链式判断请求方法 get
return res.status(200).json({
status: 'hello get child route'
});
})
.delete((req, res) => { // delete
return res.status(200).json({
status: 'hello delete child route'
});
})
module.exports = router
子路由注册路由和中间件的方式和app注册的方式完全一样。两边代码对比以下,就会发现完全一样。
示例1
请求路由
控制台打印
解释,在命中/api5这个路由之前,执行以上注册的中间件,打印全局1=>全局2=>全局3=>全局4,然后命中了/api5,打印全局5,然后执行childRouter这个子路由中间件,代码完全如app.js里写的完全一样,依次执行子路由中间件,子1=>子2=>子3=>子4,最后命中/child-api3/:id 这个路由,res响应。
代码执行流程