1、简述
啥是中间件函数,先不百度,想象理解下:在web请求-响应的循环中,会根据请求调用回调函数,并返回响应,回调函数会有嵌套调用,把这些函数固定参数形式,就是中间件。
以上是小白为了做心理上的假定合理性猜测,等深入了解后,再改正。也希望大神们留言指教。
中间件函数完成以下功能:
执行任何代码。
对请求和响应对象进行更改。
结束请求-响应周期。
调用堆栈中的下一个中间件。
Express中间件函数接受三个参数:请求对象(req)、响应对象 (res)、next()函数。
如果当前中间件函数没有结束请求-响应周期,它必须调用next()将控制传递给下一个中间件函数。否则,请求将被搁置。
2、示例:Hello World
示例功能:每次应用程序收到一个请求,它打印消息“LOGGED”到终端。
中间件加载的顺序很重要:首先加载的中间件函数也会首先执行。
如果myLogger是在到根路径的路由之后加载的,那么请求永远不会到达它,应用程序也不会打印“LOGGED”,因为根路径的路由处理程序终止了请求-响应周期。
中间件函数myLogger只是打印一条消息,然后通过调用next()函数将请求传递给堆栈中的下一个中间件函数。
var express = require('express')
var app = express()
var myLogger = function (req, res, next) {
console.log('LOGGED')
next()
}
app.use(myLogger)
app.get('/', function (req, res) {
res.send('Hello World!')
})
app.listen(3000)
3、示例:返回值创建、使用
示例功能是在web页面中显示时间,和上例代码结构一样,不同点是:这个示例演示了返回值在回调函数中如何传递。下例中req.requestTime在中间件函数中创建并设置为当前时间,在回调函数中直接使用。对于C程序员来说,这种使用方式好神奇啊!
var express = require('express')
var app = express()
var requestTime = function (req, res, next) {
req.requestTime = Date.now()
next()
}
app.use(requestTime)
app.get('/', function (req, res) {
var responseText = 'Hello World!<br>'
responseText += '<small>Requested at: ' + req.requestTime + '</small>'
res.send(responseText)
})
app.listen(3000)
4、示例:错误处理
本示例功能:创建一个中间件函数,该函数验证传入的cookie并在cookie无效时发送一个400响应。
本示例使用cookie解析器中间件从req对象解析传入的cookie,并将它们传递给cookieValidator函数。validateCookies中间件返回一个Promise,在拒绝时将自动触发我们的错误处理程序。
注意在cookieValidator(req.cookies)之后调用next()。这确保了在cookieValidator解析时,堆栈中的下一个中间件将被调用,即异步执行。
如果将任何东西传递给next()函数(字符串’route’或’router’除外),Express将认为当前请求是一个错误,并将跳过所有剩余的非错误处理路由和中间件函数。(这个功能又很突然,怎么就认为是错误了,没有看到条件判断啊?疑问先留着,待以后解决)
async function cookieValidator (cookies) {
try {
await externallyValidateCookie(cookies.testCookie)
} catch {
throw new Error('Invalid cookies')
}
}
var express = require('express')
var cookieParser = require('cookie-parser')
var cookieValidator = require('./cookieValidator')
var app = express()
async function validateCookies (req, res, next) {
await cookieValidator(req.cookies)
next()
}
app.use(cookieParser())
app.use(validateCookies)
// 错误处理
app.use(function (err, req, res, next) {
res.status(400).send(err.message)
})
app.listen(3000)
5、示例:使中间件可配置
创建hello.js中间件文件,在这个文件中使用module.exports导出中间件函数,根据传入的函数参数,在中间件函数中执行不同功能
module.exports = function (options) {
return function (req, res, next) {
// 在这里根据下面主程序传入的option1、option2来执行不同功能,实现中间件可配置。
next()
}
}
在app主程序中使用
var mw = require('./hello.js')
app.use(mw({ option1: '1', option2: '2' }))
6、应用层中间件
1)示例展示:没有指定路由,并且可以响应任何请求的中间件。
var express = require('express')
var app = express()
app.use(function (req, res, next) {
console.log('Time:', Date.now())
next()
})
2)示例展示:一个挂载在/user/:id路径上的中间件函数。该函数对/user/:id路径上的任何类型的HTTP请求执行。
app.use('/user/:id', function (req, res, next) {
console.log('Request Type:', req.method)
next()
})
3)示例展示:在/user/:id路由上,处理get请求
app.get('/user/:id', function (req, res, next) {
res.send('USER')
})
4)示例展示:中间件堆栈,即串联多个处理函数
app.use('/user/:id', function (req, res, next) {
console.log('Request URL:', req.originalUrl)
next()
}, function (req, res, next) {
console.log('Request Type:', req.method)
next()
})
5)示例展示:结束请求-响应周期
下面的示例为到/user/:id路径的GET请求定义了两条路由。第二个路由永远不会被调用,因为第一个路由最后没有调用next()结束了请求-响应周期。
app.get('/user/:id', function (req, res, next) {
console.log('ID:', req.params.id)
next()
}, function (req, res, next) {
res.send('User Info')
})
app.get('/user/:id', function (req, res, next) {
res.send(req.params.id)
})
6)示例展示:跳过中间件堆栈中其余中间件函数
可以调用next(‘route’)将控制传递给下一个路由。注意:next(‘route’)只在使用app.METHOD()或router.METHOD()函数加载的中间件函数中工作。
app.get('/user/:id', function (req, res, next) {
// 如果用户ID为0,则跳转到下一条路由
if (req.params.id === '0') next('route')
// 如果用户ID为0,则跳转到下一条路由
else next()
}, function (req, res, next) {
res.send('regular')
})
app.get('/user/:id', function (req, res, next) {
res.send('special')
})
7)示例展示:中间件函数数组
为了可重用性,中间件也可以声明在数组中。
这个例子展示了一个带有中间件子堆栈的数组,该数组处理到/user/:id路径的GET请求
function logOriginalUrl (req, res, next) {
console.log('Request URL:', req.originalUrl)
next()
}
function logMethod (req, res, next) {
console.log('Request Type:', req.method)
next()
}
var logStuff = [logOriginalUrl, logMethod]
app.get('/user/:id', logStuff, function (req, res, next) {
res.send('User Info')
})
7、路由层中间件
路由层中间件与应用层中间件的工作方式相同,只是它被绑定到express.Router()的实例上。
var router = express.Router()
通过使用router.use()和router.METHOD()函数加载路由层中间件。
下面的示例和=6=中应用层中间件的示例基本相同:
var express = require('express')
var app = express()
var router = express.Router()
// 没有挂载路径的中间件函数。对于每个向路由器的请求都会执行此代码
router.use(function (req, res, next) {
console.log('Time:', Date.now())
next()
})
// 中间件子堆栈会显示到/user/:id路径的任何类型的HTTP请求的请求信息
router.use('/user/:id', function (req, res, next) {
console.log('Request URL:', req.originalUrl)
next()
}, function (req, res, next) {
console.log('Request Type:', req.method)
next()
})
// 处理到/user/:id路径的GET请求的中间件子堆栈
router.get('/user/:id', function (req, res, next) {
// 如果用户ID为0,则跳转到下一个路由器
if (req.params.id === '0') next('route')
// 否则,将控制传递给此堆栈中的下一个中间件函数
else next()
}, function (req, res, next) {
// 渲染普通页面
res.render('regular')
})
// user/:id路径的处理程序,它呈现一个特殊的页面
router.get('/user/:id', function (req, res, next) {
console.log(req.params.id)
res.render('special')
})
// /user/:id路径的处理程序,它呈现一个特殊的页面
app.use('/', router)
要跳过路由器的其他中间件函数,调用next(‘router’)将控制传递回路由器实例。
这个例子展示了一个中间件堆栈,它处理到/user/:id路径的GET请求
var express = require('express')
var app = express()
var router = express.Router()
// 用检查谓词路由器,并在需要时提取
router.use(function (req, res, next) {
if (!req.headers['x-auth']) return next('router')
next()
})
router.get('/user/:id', function (req, res) {
res.send('hello, user!')
})
// 执行:next('router')
app.use('/admin', router, function (req, res) {
res.sendStatus(401)
})
8、错误处理中间件
错误处理中间件总是接受四个参数。必须提供四个参数将其标识为错误处理中间件函数。即使不需要使用下一个对象,也必须指定它来维护签名。否则,下一个对象将被解释为常规中间件,将无法处理错误。
app.use(function (err, req, res, next) {
console.error(err.stack)
res.status(500).send('Something broke!')
})
9、内置的中间件
Express有以下内置的中间件:
express.static:服务于静态资产,如HTML文件、图像等。
express.json:使用内置json中间件解析传入请求。
express.urlencoded:使用内置url编码中间件解析传入的请求
10、第三方中间件
使用第三方中间件为Express应用程序添加功能。
先安装,再加载:
下面的示例演示了安装和加载中间件函数cookie-parser。
$ npm install cookie-parser
var express = require('express')
var app = express()
var cookieParser = require('cookie-parser')
// 加载cookie解析中间件
app.use(cookieParser())
常用第三方中间件参见:https://www.expressjs.com.cn/resources/middleware.html