Published on

Express Middleware

Authors
  • avatar
    Name
    Deng Hua
    Twitter

目录

什么是中间件

一个 web 服务器可以被看作是一个接收请求(request)并输出响应(response)的函数。Middlewares 是在接收请求后执行的函数,然后产生一个输出,这个输出可以是最终输出,也可以被下一个中间件使用(作为下一个中间件的输入),直到循环完成。

这意味着我们可以有多个中间件,并且它们将按照声明的顺序执行。下面的 middleware Amiddleware B 之前执行,在 middleware B 之前执行 middleware C。我们可以从一个中间件传递变量到另一个中间件。

中间件的简单使用

要设置中间件,您可以为要添加的每个中间件层调用 app.use() 。中间件可以对所有路径通用,也可以仅在服务器的特定路由上触发。下面是中间件声明的示例。

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

const myLogger = function (req, res, next) {
  console.log('LOGGED');
  next();
};

app.use(myLogger);

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

app.listen(3000);

以上的中间件程序,在应用程序每次收到请求时,都会在终端上显示消息 LOGGED,中间件函数 myLogger 只是显示消息,然后通过调用 next() 函数将请求传递到堆栈中的下一个中间件函数。

中间件装载顺序遵循先后顺序,即首先装入的中间件函数首先被执行。

如果在根路径('/')的路由之后装入 myLogger,那么请求永远都不会到达该函数,应用程序也不会显示LOGGED,因为根路径的路由处理程序终止了请求/响应循环。

// ...
const myLogger = function (req, res, next) {
  console.log('LOGGED');
  next();
};

app.get('/', function (req, res) {
  app.use(myLogger); // myLogger middleware it will never be called.
  res.send('Hello World!');
});
// ...

中间件函数类型

Express 应用程序可以使用以下类型的中间件:

  • 应用层中间件
  • 路由器层中间件
  • 错误处理中间件
  • 内置中间件
  • 第三方中间件

应用层中间件

我们可以编写一个中间件函数,传入 app.use 来记录一个请求的请求方法,如下:

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

app.use((req, res, next) => {
  console.log(req.method);
  next();
});

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

app.listen(3001);

然后,当我们向 / 路由发出 GET 请求时,应用程序会打印GET


如果没有指定挂在何种路由上,则应用程序每次收到请求时都会执行该函数:

app.use(function (req, res, next) {
  console.log('Time:', Date.now());
  next();
});

此示例为一个挂载在 /user/:id 路由中的中间件函数。在 /user/:id 路由中将为任何类型的 HTTP 请求执行此函数:

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

app.use('/user/:id', function (req, res, next) {
  console.log('Request params id:', req.params.id);
  next();
});

app.listen(3001);

访问 localhost:3001/user/3 ,将打印: Request params id: 3

也可以指定具体的HTTP请求方法:

// ...
app.get('/user/:id', function (req, res, next) {
  res.send('USER');
});
// ...

或者定义多个中间件函数:

app.use('/user/:id', function (req, res, next) {
  console.log('Request params id:', req.params.id); // Request Type: 3
  next();
},function (req, res, next) {
  console.log('Request method:', req.method); // Request method: GET
});

路由器层中间件

路由器级中间件的工作方式与应用程序级中间件相同,但它们绑定到 express.Router() 实例而不是 express 实例。使用 router.use()router.METHOD() 函数装入路由器层中间件。

例如:

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

const router = express.Router();

router.use((req, res, next) => { // 使用 router.use 或者 router.get
  req.requestTime = new Date();
  next();
})
router.get('/', (req, res, next) => {
  res.json(req.requestTime);
})

app.use('/', router);
app.listen(3001);

然后,当我们向 / 路由发出请求时,我们会返回时间戳作为输出。


链接中间件和跳过路由的工作方式与应用程序级中间件相同。例如:

const express = require('express')
const app = express()
const router = express.Router();

router.use(
  (req, res, next) => {
    console.log('middleware 1 called');
    next();
  },
  (req, res, next) => {
    console.log('middleware 2 called');
    next();
  }
)
router.get('/', (req, res, next) => {
  res.json();
})

app.use('/', router);
app.listen(3001);

然后我们从每个路由中间件的 console.log 输出中看到:

middleware 1 called
middleware 2 called

如果我们注释第一个中间件的next():

router.use(
  (req, res, next) => {
    console.log('middleware 1 called');
    // next();
  },
  (req, res, next) => {
    console.log('middleware 2 called');
    next();
  }
)

则只会打印

middleware 1 called

使用 next('route') 来跳转路由,如下:

const express = require('express')
const app = express()
const router = express.Router();

// 挂载一个路由中间件,匹配GET请求,路径为"/:id"
router.get('/:id',
  (req, res, next) => {
    console.log('First middleware')
    if (req.params.id === '0') { // 匹配 '/0',命中匹配,跳转下一个路由器
      next('route');
    } else { // 否则执行本路由器的下一个中间件
      next();
    }
  },
  (req, res, next) => {
    res.send('Second middleware');
  }
)

router.get('/:id', (req, res, next) => { // 只有命中 '/0' 路径才会执行此中间件,响应字符串"Second middleware",结束此次请求
  res.send(req.params.id);
})

app.use('/', router);
app.listen(3001);

然后当我们向 '/0' 发出请求时,我们得到 0 。否则会输出 ‘Second Middleware’ 。


错误处理中间件

错误处理中间件始终采用四个自变量。必须提供四个自变量,以将函数标识为错误处理中间件函数。即使无需使用 next 对象,也必须指定该对象以保持特征符的有效性。否则,next 对象将被解释为常规中间件,从而无法处理错误。

错误处理中间件函数的定义方式与其他中间件函数基本相同,差别在于错误处理函数有四个自变量而不是三个,专门具有特征符 (err, req, res, next)

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

内置中间件

express.static(root, [options])

Express 中唯一内置的中间件函数是 express.static。此函数基于 serve-static,负责提供 Express 应用程序的静态资源。

root 自变量指定从其中提供静态资源的根目录。

以下示例将使用了 express.static 中间件,并且提供了一个详细的’options’对象(示例):

const 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));

对于每个应用程序,可以有多个静态目录:

app.use(express.static('public'));
app.use(express.static('uploads'));
app.use(express.static('files'));

第三方中间件

使用第三方中间件向 Express 应用程序添加功能。

安装具有所需功能的 Node.js 模块,然后在应用层或路由器层的应用程序中将其加装入。

以下示例演示如何安装和装入 cookie 解析中间件函数 cookie-parser

$ npm install cookie-parser
const express = require('express');
const app = express();
const cookieParser = require('cookie-parser');

app.use(cookieParser());

有关 Express 常用的第三方中间件函数的部分列表,请参阅:第三方中间件