Express中间件

179 阅读4分钟

在一些新的 Web 框架中,经常会出现 “中间件” 的概念。 中间件是介于应用系统和系统软件之间的一类软件,它使用系 统软件提供的基础服务(功能),衔接网络上应用系统的各个 部分,达到资源共享、功能共享的目的。Express 框架中的中 间件也提供了类似的功能,其处于路由请求与主要逻辑处理中间,如图下图所示:

image.png

中间件可以做很多事情,例如对所有请求的日志进行记录,类似于记录日志,这种功能拥有一定的通用性,但又不是逻辑主程序的组成部分,这类通用且非业务逻辑的功能适合使用中问件进行处理。

不仅如此,中间件也可以用于身份验证。例如传统的用户登录状态的判定,需要在服务器要在服务器的Session中存放一些该用户的信息,用户在访问某些需耍权限控制的路由时通过Session 查看是否登录,该逻辑操作也可以使用中间件进行判断。中间件仅仅查询用户是否登(Session 是否存在且没有过期》,如果登录,则执行主程序,如果没有登录,则阻止用户,这样就无须在所有的路由中判断用户的状态了。

中间件的使用

使用Express路由功能编写代码,输出“Hello World!”, 代码如下:

var express = require ('express')

var app = express()

app.get(‘/’, function (req, res){
    res.send ('Hello World!')
});

app.listen (3000)

编写一个简单的中间件,命名为checkUser, 提供用户请求信息的控制台打印功能。代码如下:

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

// 编写中间件,用于打印用户的头信息
var checkUser = function (req, res, next) {
    console.log (req.headers);
    next ();
}

// 全局使用中间件
app.use(checkuser);

app.get ('/', function (req, res){
    res.send ('Hello World!')
});

app.listen(3000)

运行程序后,当用户进行路由访问后,自动打印用户的头信息。信息如下:

{
  host: 'localhost:3000',
  connection: 'keep-alive',
  'sec-ch-ua': '"Google Chrome";v="107", "Chromium";v="107", "Not=A?Brand";v="24"',
  'sec-ch-ua-mobile': '?0',
  'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36',
  'sec-ch-ua-platform': '"macOS"',
  accept: 'image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8',
  'sec-fetch-site': 'same-origin',
  'sec-fetch-mode': 'no-cors',
  'sec-fetch-dest': 'image',
  referer: 'http://localhost:3000/',
  'accept-encoding': 'gzip, deflate, br',
  'accept-language': 'zh-CN,zh;q=0.9',
  cookie: 'io=D35qxTrBXwl8fo3wAAAh'
}

针对上例,如果使用如下代码,则表示整个App中的所有路由均使用checkUser中间件

app.use(checkUser);

但是某些中间件并不需要所有的路由都使用,例如用户登录状志的检测,不是所有的页面都要进行检测,这类中间件可以通过路由指定。例如下面的代码,/user/:id路由中的所有请求URL 均调用checkLogin中间件,而其他的路由则不调用。

var express = require('express')
var app = express()

// 编写中间件,用于打印用户的头信息
var checkUser = function (req, res, next) {
    console.log (req.headers);
    next ();
}

// 全局使用中间件
app.use(checkUser);
// 新的中间件,用来检测用户的登录状态
var checkLogin = function (req, res, next) {
    if (req.params.id === '1') {
        console.log("用户登录成功");
        next();        
    } else {
        console.log("用户未登录");
        res.send("error");
    }
}

// 对于路由调用中间件
app.use('/user/:id', checkLogin);

// 路由定义
app.get ('/', function (req, res){
    res.send('Hello World!')
});

// 新的路由定义
app.get('/user/:id', function (req, res) {
    res.send("Hello" + req.params.id);
});

app.listen(3000);

上述代码定义了新的路由/user/:id,它通关GET方式传递一个id参数,该参数使用req.params.id来获取。

在中间件中,如果id不是字符串1(这里使用严格相等),则不将该请求发送至路由处理,而是直接返回error错误信息,并且在控制台打印用户登录失败提示。如果id参数是正确的,则执行next()将请求下发至路由处理,返回的信息是Hello字符串加上该参数。

运行代码,访问根目录/时打印用户的头信息,不执行checkLogin中间件。

如果访问http://localhost:3000/user/2,则返回用户请求头文件的同时打印登录失败的提示。

如果访问http://localhost:3000/user/1,则返回正常的内容,并且提示登录成功。