Express中间件
Posted So istes immer
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Express中间件相关的知识,希望对你有一定的参考价值。
1.示例
服务器在接收到不同get请求之后,打印get请求的日志
const express = require('express')
const app = express()
// 中间件的顺序很重要
// req 请求对象 res 响应对象
// next 下一个中间件
app.use((req, res, next) => {
console.log(req.method, req.url, Date.now())
// 交出执行权,往后继续匹配执行
next()
})
app.get('/', (req,res) => {
res.send('get/')
})
app.get('/login', (req,res) => {
res.send('get/login')
})
app.get('/about', (req,res) => {
res.send('get/about')
})
app.listen(3000, () => {
console.log(`Server running at http://localhost:3000/`)
})
2.概念解析
Express的最大特色,也是最重要的一个设计,就是中间件。中间间就是一个可以访问请求对象、响应对象和调用next方法的一个函数。
Express中间件和AOP(Aspect Oriented Programming)一样,就是都需要经过一些步骤来扩展或处理一些功能,但是不去修改自己的代码,不影响原有功能
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率和可维护性
注意:如果当前的中间件功能没有结束请求-响应周期,则必须调用next()方法将控制权交给下一个中间件功能。否则,该请求将被挂起。
3.中间件分类
应用程序级别中间件
const express = require('express')
const app = express()
...
app.listen(3000, () => {
console.log(`Server running at http://localhost:3000/`)
})
①不关心请求路径
app.use(function(req, res, next) {
console.log('Time:', Date.now())
next()
})
②限定请求路径
app.use('/user/:id', function(req, res, next) {
console.log('Request Type:', req.method)
next()
})
③限定请求方法+请求路径
app.get('/user/:id', function(req, res, next) {
res.send('USER')
})
④多个处理函数
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()
})
⑤为同一个路径定义多个处理中间件
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.end(req.params.id)
})
因为send()已经结束了响应,所以后面的 res.end(req.params.id) 无效了
⑥要从路由器中间件堆栈中跳过其余中间件功能,要使用next('route')将控制权传递给下一条路由
app.get('/user/:id', function(req, res, next) {
if (req.params.id === '0') next('route')
else next()
}, function (req, res, next) {
res.send('regular')
})
app.get('/user/:id', function(req, res, next) {
res.send('special')
})
⑦中间件也可以在数组中声明为可重用
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('/users/:id', logStuff, function(req, res, next) {
res.end('User Info')
})
路由级别中间件
对上面的一个小案例进行修改,将路由部分进行一个封装
router.js
// 路由模块
const express = require('express')
const { getDb, saveDb } = require('./db')
// 1.创建路由实例,路由实例就相当于一个mini Express实例
const router = express.Router()
// 2.配置路由
router.get('/', async (req,res) => {
try {
const db = await getDb()
res.status(200).json(db.todos)
} catch(err) {
res.status(500).json({
error: err.message
})
}
})
router.get('/:id', async (req,res) => {
try {
const db = await getDb()
const todo = db.todos.find(todo => todo.id === Number.parseInt(req.params.id))
if (!todo) {
return res.status(404).end()
}
res.status(200).json(todo)
} catch (err) {
res.status(500).json({
error: err.message
})
}
})
router.post('/', async (req,res) => {
try {
// 1. 获取客户端请求体参数
const todo = req.body
// 2. 数据验证
if (!todo.title) {
res.status(422).json({
errro: 'The field title is required.'
})
}
// 3. 数据验证通过,把数据存储到db中
const db = await getDb()
const lastTodo = db.todos[db.todos.length - 1]
todo.id = lastTodo ? lastTodo.id + 1 : 1
db.todos.push(todo)
await saveDb(db)
// 4. 发送响应
res.status(201).json(todo)
} catch(err) {
res.status(500).json({
error: err.message
})
}
})
// 3. 导出路由实例
module.exports = router
app.js
const express = require('express')
const router = require('./router')
const app = express()
// 解析表单请求体:application/json
app.use(express.json())
// 解析表单请求体:application/x-www-form-urlencoded
app.use(express.urlencoded())
// 挂载路由,并给路由限定访问前缀,不限定前缀:app.use(router)
app.use('/todos', router)
app.listen(3000, () => {
console.log(`Server running at http://localhost:3000/`)
})
错误处理中间件
内置中间件
第三方中间件
以上是关于Express中间件的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 jest 在 javascript 中测试 try catch 代码并在 express 中包含带有中间件的 next() 调用?