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-Typeapplication/json格式的请求体
  • express.urlencoded() 解析Content-Typeapplication/x-www-form-urlencoded格式的请求体
  • express.raw() 解析Content-Typeapplication/octet-stream格式的请求体
  • express.text() 解析Content-Typetext/plain格式的请求体
  • express.static() 托管静态资源文件

5. 第三方中间件

早期的Express内置了很多中间件。后来Express在4.x之后移除了这些内置中间件,官方把这些功能中间件以包的形式提供出来。这样做的目的是为了保持Express本身极简灵活的特性,开发人员可以根据自己的需要去灵活的使用。
Express官方第三方插件链接:https://www.expressjs.com.cn/resources/middleware.html

posted @ 2021-11-29 10:41  木-鱼  阅读(225)  评论(0编辑  收藏  举报