NodeJS Web 框架 Express 之中间件

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

NodeJS Web 框架 Express 之中间件

参考

项目描述
哔哩哔哩黑马程序员
搜索引擎Bing

描述

项目描述
Edge109.0.1518.61 (正式版本) (64 位)
NodeJSv18.13.0
nodemon2.0.20
Express4.18.2

中间件

中间件可以理解为实现某项功能的函数或是模块。在 Express 中你可以使用一个或多个中间件来处理客户端的请求并在最后对客户端的请求进行响应。

next()

在 Express 中一个中间件若完成了它所需要完成的任务则需要在该中间件内部调用函数 next() 以结束当前中间件的运行并告知 NodeJS 运行下一个中间件或路由。

一个简单的中间件函数

function mw(req, res, next){
    console.log('【中间件函数】')
    next()
}

你可以为中间件函数提供三个参数以接收 Express 提供的三个实参这三个实参分别为

  1. 代表客户端的请求对象。
  2. 代表服务器端的响应对象。
  3. 接收 next() 函数用于结束当前中间件的运行并告知 NodeJS 运行下一个中间件或路由。

使用

全局中间件

全局中间件将对所有的客户端请求进行处理通过使用 use() 方法可以将函数注册为全局中间件。

const express = require('express');


const app = express();
// 监听本机的 9090 端口
app.listen(9090);

// 创建中间件函数
function mw(req, res, next){
    console.log('【接收到客户端的请求】');
    next()
}

// 注册中间件函数 mw 为全局中间件
app.use(mw);

// 设置路由
app.get('/', (req, res) => {
    res.send('Hello World');
})
app.get('/redheart', (req, res) => {
    res.send('Hello World');
})

在运行上述代码后无论你访问链接 127.0.0.1:9090 还是 127.0.0.1:9090/redheart 你都将终端上的控制台看到输出内容 【接收到客户端的请求】 并在响应页面中看到如下内容

响应页面

局部中间件

局部中间件不会处理所有的客户端请求它们只会处理与相关路由与该中间件绑定的路由成功匹配的客户端请求。
在使用 app.get()app.post() 等函数时你可以通过向这类函数提交多个中间件函数以将中间件注册为局部中间件。

app.get() 函数为例如果你需要将中间件 mw1mw2 注册为该路由的局部中间件可以参考如下用例

app.get('/', mw1, mw2, (req, res) => {});

// 或

app.get('/', [mw1, mw2], (req, res) => {});

共享

在一个客户端请求中reqres 是相同的你可以通过为这两个对象定义属性以在不同的函数之间传递数据。

const express = require('express');


const app = express();
// 监听本机的 9090 端口
app.listen(9090);

// 创建中间件函数
function mw(req, res, next){
    console.log('【接收到客户端的请求】');
    next()
}

// 创建中间件哈数
function mw1(req, res, next){
    req.a = 10;
    res.a = 36;
    next();
}
function mw2(req, res, next){
    req.b = req.a + res.a;
    next();
}

// 创建路由
app.get('/', mw1, mw2, (req, res) => {
    res.send(req.a + ' + ' + res.a + ' = ' + req.b);
});

执行结果

执行结果

注意事项

位置

在使用 Express 进行 Web 开发时如果你需要使用到全局中间件请将中间件放在路由之前否则该中间件 可能 无法发挥功能。
路由与请求成功匹配便不会执行之后的函数即使该路由对客户端的请求没有做出响应。

const express = require('express');


const app = express();
// 监听本机的 9090 端口
app.listen(9090);

// 创建中间件函数
function mw(req, res, next){
    console.log('【接收到客户端的请求】');
    next()
}

// 创建中间件哈数
function mw1(req, res, next){
    req.a = 10;
    res.a = 36;
    next();
}
function mw2(req, res, next){
    req.b = req.a + res.a;
    next();
}

// 创建路由
app.get('/', mw1, mw2, (req, res) => {

});

// 在路由之后创建全局中间件
app.use(() => {
    console.log('【全局中间件】');
})

执行结果

在执行上述代码后若你对 127.0.0.1:9090 进行访问你将得到一个时刻旋转的加载动画

执行效果

分析

浏览器正在等待路由的响应但路由没有对此进行响应也没有交出执行权。所以对该次请求的处理到这里就已经结束了这也导致了后面的全局中间件没有成功执行。
其实你可以再提供一个参数给路由的回调函数以获取可以使用的 next() 函数。通过使用 next() 函数你可以在合适的时候将执行权交给后续函数或路由。

const express = require('express');


const app = express();
// 监听本机的 9090 端口
app.listen(9090);

// 创建中间件函数
function mw(req, res, next){
    console.log('【接收到客户端的请求】');
    next()
}

// 创建中间件哈数
function mw1(req, res, next){
    req.a = 10;
    res.a = 36;
    next();
}
function mw2(req, res, next){
    req.b = req.a + res.a;
    next();
}

// 创建路由
app.get('/', mw1, mw2, (req, res, next) => {
    next()
});

// 在路由之后创建全局中间件
app.use(() => {
    console.log('【全局中间件】');
})

在执行上述代码后若你对 127.0.0.1:9090 进行访问你的终端将输出 【全局中间件】 这表明全局中间件已经成功执行。

虽然这样操作可以使得全局中间件在路由之后也可以正常执行但这并不是全局中间件出现的初衷对所有的客户端请求进行预处理并且这样编写代码也不利于阅读。所以在开发中请尽量将全局中间件放置在路由之前。

next()

在路由或中间件中调用 next() 函数后仍可在路由或中间件中执行后续代码。

const express = require('express');


const app = express();
// 监听本机的 9090 端口
app.listen(9090);

// 创建中间件函数
function mw(req, res, next){
    console.log('【接收到客户端的请求】');
    next()
}

// 创建路由
app.get('/', (req, res, next) => {
    res.send('Hello World');
    next();
    console.log('Finish');
})

在执行上述代码后若你对 127.0.0.1:9090 进行访问服务器将正常响应客户端的请求发送 Hello World。在执行 next() 后终端将输出 Finish

虽然使用 next() 函数后还可以执行当前中间件或路由的后续代码但请不要这样做这样会降低代码的可读性。

分类

Express 官方将中间件进行了分类具体如下

项目描述
应用级中间件app.get()app.post()app.all() 等绑定在 app 上的函数都可以认为是应用级别的中间件。
路由级中间件用于构建服务器的路由系统。
错误级中间件用于捕获在处理客户端请求过程中所产生的错误。
内置中间件由 Express 官方提供的内置中间件。
第三方中间件由第三方提供的中间件。

由于应用级中间件与路由级中间件已经在本人的本篇博客及其他博客中已讲述因此本文后续将重点关注后三类中间件。

错误级中间件

你需要向错误级中间件的回调函数提供四个参数 err, req, res, next 其中err 用于接收处理客户端请求的过程中引发的错误所对应的错误对象。

const express = require('express');


const app = express();
// 监听本机的 9090 端口
app.listen(9090);

// 创建错误级中间件函数
function mw(err, req, res, next){
    console.log('【发现错误】');
}

// 将错误级中间件注册为全局中间件
app.use(mw);

// 创建路由
app.get('/', (req, res, next) => {
	// 人为产生错误
    throw new Error('Error');
    res.send('Hello World');
})

执行结果

运行上述代码后你如果访问 127.0.0.1:9090 将得到如下内容

执行结果

浏览器中显示了错误信息。显然错误级中间件并没有成功捕获到错误。

分析

在上述示例中我们在路由之后对错误级中间件进行了全局注册。但正确的使用方式应该是将错误级中间件放置在所有的中间件及路由之后。

执行过程

Express 在执行过程中如果发现了错误会在此行引发错误的代码行向下寻找错误级中间件如果没有找到错误级中间件NodeJS 将抛出错误。
所以请尽量在所有中间件及路由之后注册错误级中间件这样能够提高服务器的稳定性避免宕机。

正确示范

const express = require('express');


const app = express();
// 监听本机的 9090 端口
app.listen(9090);

// 创建错误级中间件函数
function mw(err, req, res, next){
    console.log('【发现错误】');
}

// 创建路由
app.get('/', (req, res, next) => {
	// 人为产生错误
    throw new Error('Error');
    res.send('Hello World');
})

// 将错误级中间件注册为全局中间件
app.use(mw);

运行上述代码后你将在终端中得到如下内容

【发现错误】

内置中间件

Express 提供了如下内置中间件

项目描述
express.json()该函数用于处理客户端以 POST 方式提交的 application/json 格式的数据。
express.urlencoded()该函数用于处理客户端以 POST 方式提交的 application/x-www-form-urlencoded 格式的数据。
express.static()该函数用于托管服务器中的静态资源。

  1. express.json()express.urlencoded() 仅在 Express 4.16.0 以上的版本可用在进行后续的示例前请查看你的版本。如果你不清楚你的 Express 的版本你可用打开项目中的 package.json 文件该文件是由 npm 生成的项目信息文件进行查找。

我能够在 package.json 文件中查找到如下信息这说明我的 Express 的版本号为 4.18.2

“express”: “^4.18.2”

  1. 如果你使用的 Express 的版本比 4.16.0 更低你可用使用第三方中间件 body-parser 来对客户端通过 POST 方式提交的多种格式的数据进行处理。你可以通过如下命令使用 npm 来安装该第三方中间件
npm install body-parser
  1. express.json()express.urlencoded() 是 Express 基于第三方模块 body-parser 进行封装的。
express.urlencoded()

在此我将使用 Python 来通过 POST 方式向服务器端发送数据。当然你也可以使用 PostmanBurpSuiteHackbar v2 等工具向服务器端发送 POST 数据。

服务器端代码

const express = require('express');


const app = express();
// 监听本机的 9090 端口
app.listen(9090);


// 注册 express.urlencoded() 中间件以处理客户端
// 以 POST 方式发送的 
// application/x-www-form-urlencoded 格式的数据

// extended: false 使该中间件不要解析被嵌套的数据
app.use(express.urlencoded({ extended: false} ));

// 创建路由
app.post('/', (req, res, next) => {
    // 你可以通过 req.body 获取客户端发送的
    // application/x-www-form-urlencoded 格式的数据
    // 如果你没有使用相关的中间件处理 POST 请求
    // 数据req.body 将为 undefined。
    res.send(req.body);
})

客户端代码

# 导入 Python 第三方模块 requests 以发送请求
import requests

# 定义需要传递的数据
data = {'name': 'RedHeart', 'age': 18, 'gender': 'male'}

# 发送 POST 请求
response = requests.request('post', 'http://127.0.0.1:9090', data=data)

# 对服务器端的响应信息进行打印
print(response.text)

  1. 提交给 requests.request() 函数的 URL 需要表明使用的协议使用 http://127.0.0.1:9090 而不是 127.0.0.1:9090否则 Python 将抛出如下错误

InvalidSchema: No connection adapters were found for ‘127.0.0.1:9090’

  1. 虽然提交给服务器端的数据的类型为一个字典但 requests 会将其转换为 x-www-form-urlencoded 格式requests 默认以这种方式发送 POST 请求。

  2. x-www-form-urlencoded 的格式如下以客户端提交的数据进行演示

name=RedHeart&age=18&gender=male

执行效果

在执行上述客户端代码后Python 将在终端中打印如下内容

{“name”:“RedHeart”,“age”:“18”,“gender”:“male”}

express.json()

服务器端代码

const express = require('express');


const app = express();
// 监听本机的 9090 端口
app.listen(9090);


// 注册 express.urlencoded() 中间件以处理客户端
// 以 POST 方式发送的 
// application/x-www-form-urlencoded 格式的数据

// extended: false 使该中间件不要解析被嵌套的数据
app.use(express.urlencoded({ extended: false} ));

// 创建路由
app.post('/', (req, res, next) => {
    // 你可以通过 req.body 获取客户端发送的
    // application/x-www-form-urlencoded 格式的数据
    // 如果你没有使用相关的中间件处理 POST 请求
    // 数据req.body 将为 undefined。
    res.send(req.body);
})

客户端代码

import requests
# 导入 Python 第三方模块 json 以将字典类型的数据转换为 json 格式的数据
import json

data = json.dumps({'name': 'RedHeart', 'age': 18, 'gender': 'male'})

# 设置请求头以告知浏览器我们传递的是 application/json 的数据
headers = {'content-type': 'application/json'}

response = requests.request('post', 'http://127.0.0.1:9090', data=data, headers=headers)

print(response.text)

执行效果

在执行上述客户端代码后Python 将在终端中打印如下内容

{“name”:“RedHeart”,“age”:“18”,“gender”:“male”}

第三方中间件

你可以通过 npmNodeJS Package Manger 包管理器下载并安装第三方中间件并通过 require() 函数将其导入。第三方中间件的使用方式与内置中间件的使用方式类似。

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: NodeJS