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中间件的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 express 中间件处理所有路由?

关于Express的中间件

express-middleware

使用express.static中间件

二express中间件

如何使用 jest 在 javascript 中测试 try catch 代码并在 express 中包含带有中间件的 next() 调用?