面试官问你的Express框架中间件你还没懂?

2,396 阅读5分钟

前言

最近的学习当中,之前用Express框架写服务端的时候也没注意中间件啥的!直到面试官说你给我讲下中间件的时候,一脸懵逼的我才反应过来原来我都还没去好好研究过这个东西!这次就顺着官方框架说明,边敲边记录,总结一下Express框架的中间吧~~

Express[1] 是一个路由中间件 Web 框架,其自身只具有最低程度的功能:Express 应用程序基本上是一系列中间件函数调用。

中间件函数能够访问请求对象[2] (req)、响应对象[3] (res) 以及应用程序的请求/响应循环中的下一个中间件函数。下一个中间件函数通常由名为 next 的变量来表示。

示例:

// 通用中间件
function middleware(req, res, next) {
  // 中间处理逻辑 .....
    next();
}
// req为请求  res为响应  next为调用下一个中间件函数

中间件函数可以执行以下任务

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

如果在当前中间件执行完毕后,没有结束上面的请求/响应循环,那么中间件必须调用next()函数将该请求的控制权交给下一个中间件。否则该请求将保持挂起状态。

Express框架当中根据中间件使用方式的不同可以分为以下几类:

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

一、应用层中间件

中间件可以使用app.use()app.METHOD() 函数绑定到应用程序对象的实例。其中,中间件可以在方法的内部定义,也可以在外部定义,在上面两种方法内只是引用;METHOD指代的是请求当中小写的HTTP方法。

中间件函数,后续示例基于以下实例:

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

function middleware(req, res, next) {
  console.log('请求来了...');
  next();
}

1. app.use()方法绑定

没有安装路径的中间件函数。应用程序每次收到请求时执行该函数。

app.use(middleware);

安装在 /user/:id 路径中的中间件函数。在 /user/:id 路径中为任何类型的 HTTP 请求执行此函数。

app.use('/user/:id', middleware)

上述的中间件写法属于在外部定义,当然也可以在内部如下定义:

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

2. app.method()方法绑定

这种方法是我们常用的方法,一般我们都会用这种方法对特定请求进行处理。

app.get('/user/:id', (req, res, next) => {
  console.log(req.params.id);
  res.send(req.params.id)
})

3. 小结

多中间件

以下是在安装点使用安装路径装入一系列中间件函数的示例。只要其中一个结束了请求/响应循环,则中间件子堆栈的调用停止。

app.use('/user/:id', middleware, middleware2 ...)

跳跃调用

可以调用 next('route') 将控制权传递给下一个路由。适用于要跳过路由器中间件堆栈中剩余的中间件函数的场景。

next('route') 仅在使用 app.METHOD()router.METHOD() 函数装入的中间件函数中有效。

二、路由器层中间件

路由器层中间件的工作方式与应用层中间件基本相同,差异之处在于它绑定到 express.Router() 的实例。

var app = express();
var router = express.Router();

// 路由中间件处理逻辑...

// 挂在路由中间件到应用程序实例
app.use('/', router);

使用 router.use()router.METHOD() 函数装入路由器层中间件。和上述应用层中间件用法大致相似:

没有安装路径的中间件,为每个请求执行此中间件:

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

定义了路径,并调用中间件子堆栈处理请求:

router.get('/user/:id', function (req, res, next) {
  // 如果用户ID为0  则跳到下一个路由
  if (req.params.id == 0) next('route');
  // 否则将控制权交给该子堆栈中的下一个中间件处理
  else next(); 
}, function (req, res, next) {
  // 渲染页面
  res.render('regular');
});

三、错误处理中间件

错误处理与其他中间件函数最大的差别在于有四个自变量 (err, req, res, next)

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

四、内置中间件

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

app.use(express.static(rootDir, options));

其中rootDir是提供静态资源的根目录,options是可选的对象,可以具有以下属性:

参考代码:

app.use(express.static('public', {
  dotfiles: 'ignore',
  etag: false,
  extensions: ['htm', 'html'],
  index: false,
  maxAge: '1d',
  redirect: false,
  setHeaders: function (res, path, stat) {
    res.set('x-timestamp', Date.now());
  }));

五、第三方中间件

除了自定义的中间件和内置中间件,在开发过程当中肯定少不了的会用到第三方的一些中间件来向 Express 应用程序添加功能。

常用的第三方插件包括 body-parser, compression, cookie-parser, cookie-session等。

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

安装 cookie-parser中间件示例:

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

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

// load the cookie-parsing middleware
app.use(cookieParser());

小结

中间件的本质是一个函数,在服务端处理请求的时,在收到请求和返回响应的过程中为我们做一些工作。每一个中间件交个下一个中间件的是对请求的控制权。

参考资料

[1]

Express: https://expressjs.com/zh-cn/

[2]

请求对象: https://expressjs.com/zh-cn/4x/api.html#req

[3]

响应对象: https://expressjs.com/zh-cn/4x/api.html#res


本文首发于公众号:达哥的号