Nodejs Express 中间件三部曲之一:Middleware 中间件的使用和分类

1,604 阅读5分钟

本文已参与掘金创作者训练营第三期「话题写作」赛道,详情查看:掘力计划|创作者训练营第三期正在进行,「写」出个人影响力

中间件的字面意思是放在软件一层和另一层中间的任何东西。Express 中间件(Middleware)是在 Express 服务每个请求的生命周期内执行的函数。

体验 Express 中间件

我们简单体验一下中间件。首先,创建一个项目,进入目录执行

npm init
npm install express --save

创建一个 server.js ,其内容如下:

const express = require('express');
const app = express();

app.get('/', (req, res, next) => {
  res.send('欢迎回家');
});

app.listen(3000);

通过 node server.js 启动服务,访问 http://localhost:3000,您应该会在浏览器中看到“欢迎回家”。

(req, res, next) => {
  res.send('欢迎回家');
}

以上示例中的这个匿名函数就是一个中间件 函数,它结束了请求,向客户端返回了 欢迎回家 字符串。

中间件的格式和用处

Express 中间件函数可以访问请求对象 (req)、响应对象 (res) 和下一个中间件函数。下一个中间件函数通常由名为 next 的变量表示。

image.png

function middware(req, res, next) => {
  console.log(req);
  next();
}

如果一个请求匹配了多个中间件函数,那么每个中间件将按顺序依次执行,直至请求结束。如下图所示。

image.png

当然在每个中间件过程中,也可以自己终止请求,这样下一个中间件函数就不会被执行到了。

middle-ware.jpeg

中间件功能可以执行以下任务:

  • 执行任何代码。
  • 对请求和响应对象进行更改。
  • 结束请求-响应循环。
  • 调用堆栈中的下一个中间件。

如果当前中间件函数没有结束请求-响应循环,则必须调用 next() 将控制权传递给下一个中间件函数。 否则,请求将被挂起。

通过中间件,我们能更有序的组织代码。

中间件的分类

根据中间件的使用场景,大致可以分为以下几类中间件

  • 应用级别中间件 app.use
  • 路由级别中间件 router.use
  • express 内置中间件 express.static,express.json,express.urlencoded
  • 错误处理中间件 app.use(err,req,res,next)
  • 第三方中间件 bodyparser,cookieparser

应用级别中间件

简单日志示例 log.js:

const express = require('express');

// custom middleware create
const LoggerMiddleware = (req,res,next) =>{
    console.log(`Logged  ${req.url}  ${req.method} -- ${new Date()}`)
    next();
}

const app = express()

// application level middleware
app.use(LoggerMiddleware);


// users route
app.get('/users',(req,res)=>{
    res.json({
        'status':true
    })
})

app.listen(3002,(req,res)=>{
    console.log('server running on port 3002')
})

启动服务 node log.js, 访问 localhost:3002 localhost:3002/users,可以看到如下日志:

server running on port 3002
Logged  /  GET -- Sat Aug 21 2021 11:54:28 GMT+0800 (GMT+08:00)
Logged  /users  GET -- Sat Aug 21 2021 11:56:18 GMT+0800 (GMT+08:00)

路由级别中间件

路由器级中间件的工作方式与应用级中间件相同,只是它绑定到 express.Router() 的实例。

const router = express.Router()

使用 router.use() 、及 router.post()类似的函数加载路由器级中间件。

const express = require('express');

const app = express();

const router = express.Router()

router.use((req,res,next)=>{
    console.log("Time:",new Date())
    next()
})


router.get("/user/:id",(req,res,next)=>{
    console.log('Request URL:', req.originalUrl)
    next()
},(req,res,next)=>{
    console.log('Request Type:', req.method)
    next()
},(req,res)=>{
    res.json({
        status:true,
        id:req.params.id
    })
})


app.use('/',router)

app.listen(3000,(req,res)=>{
    console.log('server running on 3000')
})
server running on 3000
Time: 2021-08-21T04:02:19.781Z
Request URL: /user/1
Request Type: GET
Time: 2021-08-21T04:02:24.002Z
Request URL: /user/123
Request Type: GET

内置中间件

Express 内置了以下中间件功能:

  • express.static 提供静态资产,例如 HTML 文件、图像等。
  • express.json 使用 JSON 负载解析传入请求。 注意:适用于 Express 4.16.0+
  • express.urlencoded 使用 URL 编码的有效负载解析传入请求。 注意:适用于 Express 4.16.0+

示例:以下操作将 public 默认为静态资源目录,并做一些配置。

var options = {
  dotfiles: 'ignore',
  etag: false,
  extensions: ['htm', 'html'],
  index: false,
  maxAge: '1d',
  redirect: false,
  setHeaders: function (res, path, stat) {
    res.set('x-timestamp', Date.now())
  }
}

app.use(express.static('public', options))

错误处理中间件

Express JS 自带默认的错误处理参数,定义错误处理中间件函数的方式与其他中间件函数相同,只是错误处理函数有四个参数而不是三个:

app.get('/my-other-thing', (req, res, next) => {
  next(new Error('I am passing you an error!'));
});

app.use(function (err, req, res, next) {
    console.error(err.stack)
    res.status(500).send('Something broke!')
})

第三方中间件

在某些情况下,我们会向后端添加一些额外的功能。安装特定的第三方中间件,然后在应用程序级别或路由器级别将其加载到您的应用程序中。譬如 bodyparser,cookieparser。示例:

var express = require('express')
var cookieParser = require('cookie-parser')

var app = express()
app.use(cookieParser())

app.get('/', function (req, res) {
  // Cookies that have not been signed
  console.log('Cookies: ', req.cookies)

  // Cookies that have been signed
  console.log('Signed Cookies: ', req.signedCookies)
})

app.listen(8080)

带 cookie 访问curl http://127.0.0.1:8080 --cookie "Cho=Kim;Greet=Hello",可以看到如下效果:

Cookies:  { Cho: 'Kim', Greet: 'Hello' }
Signed Cookies:  [Object: null prototype] {}

最后

通过以上的学习,希望对大家有所帮助。原创不易,感谢阅读,欢迎点赞收藏。

以下是笔者之前的文章,有兴趣可以继续阅读。

参考致谢