Express中间件理解、分类和使用
中间件的理解
中间件:在流程中加入或者减少一个或者多个步骤,而不影响原有的功能
引入中间件
app.use(function(req, res, next) {})
使用中间件的完整写法其实是
app.use('/', function(req, res, next) {})
,根路由一般可以省略
非根路由比如/about
,就只能这样写app.use('/about', function(req, res, next) {})
这种写法和路由很像,其实所有的路由也是中间件,路由可以做的,中间件都可以做,比如他们function
的参数都是req, res, next
,都可以使用next
,都可以res.send()
返回数据,唯一不同的就是路由有特定的请求方法
app.method(url, function(req, res, next) {})
不论路由还是中间件,都是按顺序匹配的。一般而言,所有的中间件都应该放在路由前使用,中间件的作用可以理解为公共方法、统一处理或者路由拦截等概念,与AOP(面向切面编程)概念相同
中间件分类
1. 应用程序级别中间件
不作任何限定的中间件
app.use((req, res, next) => {
console.log("Time", Date.now())
next()
})
限定请求路径
app.use('/user/:id',(req, res, next) => {
console.log("Request Type", req.method)
next()
})
限定请求方法 + 路径
app.get('/user/:id',(req, res, next) => {
res.send('Hello World')
})
多个处理函数
app.use(
"/user/:id",
(req, res, next) => {
console.log("Request URL", req.originalUrl)
next()
},
(req, res, next) => {
console.log("Request Type", req.method)
next()
}
)
为同一个路径定义多个处理中间件
app.get(
"/user/:id",
(req, res, next) => {
console.log("ID", req.params.id)
next()
},
(req, res, next) => {
res.send("User Info")
next()
}
)
app.get("/user/:id", (req, res, next) => {
console.log("123")
// res.end(req.params.id)
})
要从路由器中间件堆栈中跳过其余中间件功能,请调用next('route')将控制权传递给下一条路由
// next('route')仅在使用app.METHOD()或router.METHOD()函数加载的中间函数中有效
app.get(
"/user/:id",
(req, res, next) => {
if (req.params.id === "0") next("route")
else next()
},
(req, res, next) => {
res.send("regular")
}
)
app.get("/user/:id", (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("Requset Type", req.method)
next()
}
const logStuff = [logOriginalUrl, logMethod]
app.get("/user/:id", logStuff, (req, res, next) => {
res.send("User Info")
})
2. 路由器级别中间件
// 1. 通过express创建router实例
const router = express.Router()
// 2. 配置路由
router.get('/user', (req, res) => {
res.send('get /user')
})
// 3. 导出路由实例
module.exports = router
// 4. 在app.js中将导出的router实例引入,并挂载到express实例上
const router = require('./router')
app.use('/todos', router) // 给router实例中所有的路由加上访问前缀/todos
3. 错误处理中间件
一般是在所有中间件之后挂载错误处理中间件。错误处理中间件函数,必须使用四个参数
app.use((err, req, res, next) => {
console.error(err)
res.status(500).send('Something broke!')
})
如果将任何内容传递给next()函数('route'除外),Express都会将当前请求视为错误,并且跳过所有剩余的非错误处理路由和中间件函数。
app.get("/todos", async (req, res, next) => {
try {
const db = await getDb()
res.status(200).json(db.todos)
} catch (err) {
// next() // 往后匹配下一个中间件
// next('route') // 往后匹配当前中间件堆栈中的下一个
// 跳过所有剩余的非错误处理路由和中间件函数
next(err)
}
})
app.use((err, req, res, next) => {
console.log("错误", err)
res.status(500).json({
error: err.message
})
})
关于处理404路由匹配不到情况,通常会在所有的路由之后配置处理 404 的内容。
app.use((req, res, next) => {
res.status(404).send("404 Not Found.")
})
4. 内置中间件
express.json()
解析Content-Type为application/json
格式的请求体express.urlencoded()
解析Content-Type 为application/x-www-form-urlencoded
格式的请求体express.raw()
解析Content-Type为application/octet-stream
格式的请求体express.text()
解析Content-Type为text/plain
格式的请求体express.static()
托管静态资源文件
5. 第三方中间件
早期的Express内置了很多中间件。后来Express在4.x之后移除了这些内置中间件,官方把这些功能中间件以包的形式提供出来。这样做的目的是为了保持Express本身极简灵活的特性,开发人员可以根据自己的需要去灵活的使用。
Express官方第三方插件链接:https://www.expressjs.com.cn/resources/middleware.html