中间件

中间件是不直接接收请求,也不直接发送响应,而是在这之间处理一个中间过程的组件。

在这里插入图片描述

当一个请求到来,会经过多个中间件进行处理,后一个中间件拿到前一个中间件的处理结果,进行再处理,处理完毕后发给下一个中间件,以此类推。直到所有任务执行完毕,最后得到一个响应,再发送回给客户端。

express中,所谓的中间件不过就是一个函数,接收参数返回结果。

全局中间件函数定义:

function (req, res, next){
    next()
}

其接收三个参数,前两个参数与请求的响应函数一致,next是中间件必须有的参数,并且在中间件函数的末尾,必须调用next()方法,这样才会调用下一个中间件函数。


全局中间件

定义好中间件函数后,可以通过app.use将其注册到服务中。

app.use(Middleware)

其中Middleware是一个中间件函数。

这种被直接注册到app.use上的中间件,称为全局生效中间件客户端发起的任何请求,都会触发全局中间件

app.use(function (req, res, next){
    console.log("Middleware running...")
    next()
})

app.get('/', function (req, res){
    console.log("get / success")
})

app.get('/index.html', function (req, res){
    console.log("get /index.html success")
})

app.listen(80, () => {
    console.log("create web server success")
})

以上服务,定义了一个匿名的中间件函数,并且注册到app.use中,两个响应函数分别响应//index.html

在浏览器中访问这两个地址,查看控制台:

Middleware running...
get / success
Middleware running...
get /index.html success

两个请求都触发了中间件,并且中间件比路由先执行。

中间件之间又要如何传递参数?在从收到请求到发送响应期间,所有的中间件共享同一个reqres对象

因此上游的中间件可以把属性或方法添加到这两个对象中,然后下游的中间件只需要访问这两个对象就可以拿到参数。

示例:

app.use(function (req, res, next){
    console.log("Middleware running...")
    req.sendStr = 'hello world!'
    next()
})

app.get('/', function (req, res){
    console.log("get / success")
    res.send(req.sendStr)
})

app.get('/index.html', function (req, res){
    console.log("get /index.html success")
    res.send(req.sendStr)
})

这个代码,在第一个中间件处,给req添加了一个对象sendStr = 'hello world!',在最后的路由函数中,就可以直接获取req.sendStr并发送出去。

如果要定义多个中间件,只需要多次使用app.use注册即可:

app.use(function (req, res, next){
    console.log("Middleware 1 running...")
    next()
})

app.use(function (req, res, next){
    console.log("Middleware 2 running...")
    next()
})

app.get('/', function (req, res){
    console.log("get / success")
})

多个中间件会以定义的顺序依次执行,访问/的输出结果:

Middleware 1 running...
Middleware 2 running...
get / success

可以看到,先执行了Middleware 1后执行Middleware 2,最后执行路由函数。


局部中间件

如果不使用app.use注册中间件,而是把中间件注册到某个路由上,称为局部中间件,这种中间件只在某个路由触发时执行。

注册局部中间件直接将中间件函数写入到getpost方法中:

app.get('url', Middleware, function(){})
app.post('url', Middleware, function(){})

示例:

const vm1 = function(req, res, next){
    console.log("Middleware 1 running...")
    next()
}

app.get('/', vm1, function (req, res){
    console.log("get / success")
})

app.get('/index.html', function (req, res){
    console.log("get /index.html success")
})

app.listen(80, () => {
    console.log("create web server success")
})

以上代码为get /路由绑定了中间件vm1,但是get /index.html没有绑定。

访问get /

Middleware 1 running...
get / success

访问get /index.html

get /index.html success

此时只有get /触发了局部中间件。

如果要定义多个局部中间件,有两种形式:

app.get('url', Middleware1, Middleware2, function(){})
app.post('url', [Middleware1, Middleware2], function(){})

第一种是直接传入多个中间件函数,第二种是把多个中间件函数作为一个数组进行传入。执行顺序从前往后。

一些中间件的注意事项:

  1. 中间件必须在路由之前注册
  2. 所有中间件必须调用next()方法
  3. next()方法后面不要再写其他逻辑,作为整个函数的结尾

分类

Express官方将中间件的用法,分为了五大类:

  1. 应用级中间件
  2. 路由级中间件
  3. 错误级中间件
  4. Express内置中间件
  5. 第三方中间件

应用级中间件

只要中间件被绑定到app上,就是应用级中间件,先前讲解的两个全局和局部中间件,都属于应用级中间件。

路由级中间件

如果中间件被绑定到express.Router对象上,那么就是路由级中间件。

示例:

const app = express()
const router = express.Router()

// 路由级中间件
router.use(function (req, res, next){
	next()
})

app.use('/', router)

在博客 [Node.js:Express 服务 & 路由] 讲解路由模块化时,讲解过这个对象,如果想把路由进行模块化,就在一个新的模块中专门绑定路由到这个Router对象上,然后再把这个对象共享给外部。


错误级中间件

错误级中间件专门用于捕获整个项目发送的异常错误,防止项目崩溃。

函数格式:

function (err, req, res, next){
    next()
}

在基本的中间件函数上,第一个参数增加一个err参数,用于捕获全局的异常。

示例:

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

app.get('/', function (req, res){
    throw new Error(' / create a error!') // 抛出异常
    res.send('success')
})

// 注册错误级中间件
app.use(function (err, req, res, next){
    res.send('something happen: ' + err.message)
})

app.listen(80, () => {
    console.log("create web server success")
})

以上代码,在访问get /时,会抛出一个异常,如果不处理项目就崩溃了。

随后为该服务注册了一个错误级中间件,在中间件内部err就是异常对象,直接把异常信息发送回给客户端。

注意:只有错误级别的中间件才可以在路由之后注册,其余的中间件都必须在路由前注册

输出结果:

在这里插入图片描述

可以看到,此处得到的结果是错误信息,说明错误被处理了。


内置中间件

Express内置了三个中间件,这些中间件可以快速完成某些功能:

  • express.static:托管静态资源
  • express.json:解析json格式的请求数据
  • express.urlencoded:解析URL-encoded格式的请求数据

其中第一个中间件已经在之前详细讲解过了,接下来看看后两个中间件的功能:

启动如下服务:

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

app.post('/user', function (req, res){
    console.log(req.body)
})

app.listen(80, () => {
    console.log("create web server success")
})

其中post /user路由,会把收到的请求的请求体输出到控制台。

使用postman发送一个POST请求,请求内容为一个json字符串:

{
    "name": "张三",
    "age": 18
}

控制台输出结果:

undefined

奇怪了,明明发送了一个json字符串,为什么请求体得到的是一个undefined

如果不配置解析数据的中间件,那么req.body = undefined

而这个解析数据的中间件,就是express.json或者express.urlencoding

express.json

想要解析刚才的json格式数据,只需要将express.json注册到服务上即可:

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

app.use(express.json()) // 注册处理数据的中间件

app.post('/user', function (req, res){
    console.log(req.body)
})

app.listen(80, () => {
    console.log("create web server success")
})

再次发送相同的请求,控制台输出结果就是正确的字符串了。

express.urlencoded

postman发送以下数据:

在这里插入图片描述

以键值对的形式发送数据,如果依然使用express.json进行解析,虽然req.body不是undefined了,但是由于检测不到json字符串,最后会得到一个空对象。

这种键值对形式的数据,就需要express.urlencoded中间件了:

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

app.use(express.urlencoded({ extended: false }))

app.post('/user', function (req, res){
    console.log(req.body)
})

app.listen(80, () => {
    console.log("create web server success")
})

使用urlencoded时,要传入一个对象,属性值固定为extended: false

发起同样的请求,输出结果:

在这里插入图片描述

最后发送的数据,就被转化为了一个对象。


上一篇:算子级血缘助企业数据管理“自动化、精细化、智能化”


下一篇:标签之文字排版,图片,链接,音视频(HTML) 基础版