从零搭建 Node.js企业级 Web 服务器:Express中间件

开发中间件

中间件 函数是能够访问请求对象req)、响应对象res)和应用程序请求-响应周期中的next函数的函数。next函数是Express路由器中的函数,当调用时,它执行继当前中间件之后的中间件。如果当前中间件函数没有结束请求-响应周期,它必须调用next()将控制权传递给下一个中间件函数。否则,请求将被搁置。 下面展示一下官网的中间件函数调用的元素的图:

在上一节项目基础上,我们把server上多于的代码删掉,来作为这一节讲解的基础。

改造server代码

js 复制代码
const express = require('express');
const { resolve } = require('path');

const server = express();
server.get('/home', function (req, res) {
  res.send('Hello World!');
});

const port = parseInt(process.env.PORT || '8080');
const publicDir = resolve('public');
server.use(express.static(publicDir));
server.listen(port, () => console.log(`Listening on port ${port}`));

实现第一个中间件

打印请求时间戳 接下来,我们将创建一个名为requestTime的中间件函数,并将名为requestTime的属性添加到请求对象中。

js 复制代码
const requestTime = function (req, res, next) {
  req.requestTime = Date.now();
  next();
};
server.use(requestTime);

这是一个最简单的中间件,上述对 next() 函数的调用。调用此函数会触发应用程序中的下一个中间件函数。next() 函数不是 Node.jsExpress API 的一部分,而是传递给中间件函数的第三个参数。next() 函数的名称可以是任何名称,但按照约定,它总是被命名为 next。需要加载中间件函数,还需要调用server.use()指定中间件函数。相当于将该中间件注册到应用服务。

js 复制代码
// src/server.js
const express = require('express');
const { resolve } = require('path');
const server = express();

+ const requestTime = function (req, res, next) {
+     req.requestTime = Date.now();
+     next();
+ };
+ server.use(requestTime);
server.get('/home', function (req, res) {
  res.json({
    data: 'Hello World!',
    t: req.requestTime,
  });
});

const port = parseInt(process.env.PORT || '8080');
const publicDir = resolve('public');
server.use(express.static(publicDir));
server.listen(port, () => console.log(`Listening on port ${port}`));

执行yarn dev后,使用postman测试一下/home接口。

express还提供了可配置的中间件。如果需要使用配置中间件,我们需要导出一个接受选项对象或其他参数的函数,然后根据输入参数返回中间件实现。接下来我们新增一个中间件文件夹,在其中新建一个中间件命名为my-middleware.js

bash 复制代码
$ tree -a
├── package.json
├── public
│   └── index.html
├── src
│   ├── middleware
│   │   └── my-middleware.js
│   └── server.js
└── yarn.lock
js 复制代码
//my-middleware
// 创建可配置的中间件
function configurableMiddleware(config) {
  // 返回实际的中间件处理函数
  return function (req, res, next) {
    // 使用配置对象中的参数来决定中间件的行为
    if (config.enableLogging) {
      req.logger = `[${new Date()}] Request received for ${req.method} ${
        req.url
      }`;
    }

    if (config.authorizationRequired && !req.user) {
      return res.status(401).send('Unauthorized');
    }

    // 继续请求处理链
    next();
  };
}

module.exports = configurableMiddleware;

server中使用该中间件

js 复制代码
const express = require('express');
const { resolve } = require('path');
const server = express();
+ const configurableMiddleware = require('./middleware/my-middleware');

// 配置中间件
+ const middlewareConfig = {
+   enableLogging: true,
+   authorizationRequired: false,
+ };

const requestTime = function (req, res, next) {
  req.requestTime = Date.now();
  next();
};
server.use(requestTime);
+ server.use(configurableMiddleware(middlewareConfig));

server.get('/home', function (req, res) {
  res.json({
    data: 'Hello World!',
    t: req.requestTime,
+    logger: req.logger,
  });
});

const port = parseInt(process.env.PORT || '8080');
const publicDir = resolve('public');
server.use(express.static(publicDir));
server.listen(port, () => console.log(`Listening on port ${port}`));

中间件的使用

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

应用程序级中间件

使用server.use()server.METHOD()函数将应用程序级中间件绑定到应用程序对象的实例,其中METHOD是中间件函数以小写方式处理的请求(如GETPUTPOST)的HTTP方法。

温馨提示: 如果当前中间件函数没有结束请求-响应周期,它必须调用next()将控制权传递给下一个中间件函数。否则,请求将被搁置。

我们在在上面实现的第一个中间件就是输入应用程序中间件,展示了一个没有挂载路径的中间件函数。每次应用程序收到请求时都会执行该功能。

js 复制代码
const requestTime = function (req, res, next) {
  req.requestTime = Date.now();
  next();
};
server.use(requestTime);

下面我们实现一个在某个路径上触发的中间件,此示例显示了挂载在/example/a路径上的中间件函数。该函数为/example/a路径上的任何类型的HTTP请求执行。

js 复制代码
server.use('/example/a', function (req, res, next) {
  console.log('Request Type:', req.method);
  res.send('/example/a');
});

如果需要对某个特定的HTTP方法进行处理,可以参照下面这个例子,只处理对/example/b路径的GET请求,相应的POSTDELETEPUT方法也是一样的。

js 复制代码
server.get('/example/b', function (req, res, next) {
  res.send('/example/b')
})

当然我们也可以使用路由中间件子堆栈,该子堆栈将/stack路径的任何类型的HTTP请求的请求信息进行打印。

js 复制代码
server.use(
  '/stack',
  function (req, res, next) {
    console.log('Request URL:', req.originalUrl);
    next();
  },
  function (req, res, next) {
    console.log('Request Type:', req.method);
    res.send(req.method);
  },
);

相应的我们也可以为一条路径进行多个定义多个路由,请看下面示例

js 复制代码
server.get(
  '/example/c',
  function (req, res, next) {
    console.log('ID:', req.params.id);
    next();
  },
  function (req, res, next) {
    req.requestTime = Date.now();
    next();
  },
);

server.get('/example/c', function (req, res, next) {
  res.json({
    url: req.url,
    t: req.requestTime,
  });
});

当我们路由处理程序为一条路径定义多个路由,且需要在某个特定的条件下从路由器中间件堆栈中跳过其余的中间件功能时,我们可以使用next('route')将控制权传递给下一个路由,但是切记next('route')仅适用于使用app.METHOD()router.METHOD()函数加载的中间件函数。 下面我们写个示例展示一下:

js 复制代码
server.get(
  '/user/:id',
  function (req, res, next) {
    // 当id为0时我们将控制权传递给下一个路由
    if (req.params.id === '0') next('route');
    else next();
  },
  function (req, res, next) {
    res.send('regular');
  },
);

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

中间件也可以在数组中声明,以便重用。

js 复制代码
function logOriginalUrl(req, res, next) {
  console.log('Request URL:', req.originalUrl);
  next();
}

function logMethod(req, res, next) {
  console.log('Request Type:', req.method);
  next();
}

var logStuff = [logOriginalUrl, logMethod];
server.get('/log', logStuff, function (req, res, next) {
  res.send('log Info');
});

路由中间件

其实路由器级中间件的工作方式与应用程序级中间件相同,只是它绑定到express.Router()的实例。

js 复制代码
const router = express.Router()

使用router.use()router.METHOD()函数加载路由器级中间件。

我们只需将上面显示的应用程序级中间件的app换成由器级中间件router,这里我们不做过多的讲解,大家可以查看官方文档

错误处理中间件

注意: 错误处理中间件总是需要四个 参数。您必须提供四个参数来将其标识为错误处理中间件函数。即使您不需要使用next对象,也必须指定它来维护签名。否则,next对象将被解释为常规中间件,并且将无法处理错误。

js 复制代码
server.use(function (err, req, res, next) {
  console.error(err.stack)
  res.status(500).send('Something broke!')
})

错误处理中间件,我们在后面搭建应用程序会有一个专门的栏目去做讲解,这里只是让大家有一个初步的认识。

内置中间件

Express.js 提供了一些内置中间件,这些中间件是 Express 核心的一部分,无需额外安装,可以直接在你的应用程序中使用。以下是一些常见的内置中间件:

  1. express.static(root, [options])

    这是一个用于提供静态文件(如 HTML、CSS、JavaScript、图像等)的中间件。你可以将静态文件的根目录传递给 express.static 函数,然后通过相对于根目录的路径来访问这些文件。例如:

    javascript 复制代码
    app.use(express.static('public'));

    上述代码将允许你在浏览器中访问 http://localhost:3000/public/file.html,以获取位于项目根目录下 public 文件夹中的 file.html

  2. express.json([options])

    这个中间件用于解析请求的 JSON 数据。它会解析请求的主体内容,将 JSON 数据转换为 JavaScript 对象,并将其附加到 req.body 上。这使得你可以轻松地处理客户端发送的 JSON 数据。

    javascript 复制代码
    app.use(express.json());
  3. express.urlencoded([options])

    这个中间件用于解析来自表单的 URL 编码数据。它会解析请求的主体内容,将 URL 编码的数据转换为 JavaScript 对象,并将其附加到 req.body 上。这使得你可以处理来自 HTML 表单的数据。

    javascript 复制代码
    app.use(express.urlencoded({ extended: false }));
  4. express.Router()

    这是一个用于创建路由组的中间件。你可以使用 express.Router() 创建一个新的路由对象,然后将路由定义在这个对象上。这有助于将路由模块化和组织成更可维护的结构。

    javascript 复制代码
    const router = express.Router();
    
    router.get('/', (req, res) => {
      res.send('This is a router');
    });
    
    app.use('/myrouter', router);

    上述代码创建了一个名为 /myrouter 的路由组,并将路由组添加到 Express 应用程序中。

  5. express.raw([options])express.text([options])

    这些中间件用于处理原始数据和文本数据。express.raw() 中间件允许你处理二进制数据,而 express.text() 中间件用于处理纯文本数据。

这些是 Express.js 内置的一些常用中间件。你可以根据你的应用程序需求选择性地使用这些中间件,以简化开发和处理不同类型的请求数据。同时,Express 还允许你编写自定义中间件,以满足特定的需求。

第三方中间件

第三方中间件是由 Express.js 社区或其他开发者开发的中间件,用于扩展 Express 应用程序的功能。这些中间件通常以包的形式发布到 npm,可以方便地集成到你的 Express 应用程序中。以下是一些常见的第三方中间件以及它们的功能:

  1. body-parser

    这是一个用于解析请求主体的中间件,支持解析 JSONURL 编码数据、原始数据和文本数据。它可以帮助你轻松地处理 POST 请求中的数据。

    javascript 复制代码
    const bodyParser = require('body-parser');
    app.use(bodyParser.json()); // 解析 JSON 数据
    app.use(bodyParser.urlencoded({ extended: false })); // 解析 URL 编码数据
  2. morgan

    这是一个 HTTP 请求日志记录中间件,它可以记录每个进入服务器的请求的信息,如请求方法、URL、响应状态码等。这对于调试和监控应用程序非常有用。

    javascript 复制代码
    const morgan = require('morgan');
    app.use(morgan('combined')); // 记录详细请求信息
  3. cookie-parser

    这个中间件用于解析客户端发送的 cookie,并将解析后的 cookie 数据附加到请求对象的 req.cookies 上。它是处理会话管理和用户身份验证的重要工具。

    javascript 复制代码
    const cookieParser = require('cookie-parser');
    app.use(cookieParser());
  4. helmet

    Helmet 是一个用于增强 Express 应用程序安全性的中间件。它通过设置 HTTP 头部来增加安全性,包括防止常见的攻击,如跨站点脚本(XSS)攻击、点击劫持等。

    javascript 复制代码
    const helmet = require('helmet');
    app.use(helmet());
  5. cors

    CORS(跨域资源共享)中间件用于处理跨域请求。它允许你控制哪些域名可以访问你的应用程序,并配置允许的 HTTP 方法和头部。

    javascript 复制代码
    const cors = require('cors');
    app.use(cors());
  6. express-session

    这个中间件用于处理会话管理。它可以将会话数据存储在服务器端,并为每个客户端分配唯一的会话标识符,以跟踪用户状态。

    javascript 复制代码
    const session = require('express-session');
    app.use(session({ secret: 'your-secret-key', resave: false, saveUninitialized: true }));
  7. passport

    Passport 是一个用于身份验证的中间件,支持多种身份验证策略,如本地策略、OAuthOpenID 等。它可以帮助你实现用户身份验证和授权。

    javascript 复制代码
    const passport = require('passport');
    app.use(passport.initialize());
    app.use(passport.session());

这些是一些常见的第三方中间件,但 npm 上还有许多其他中间件可供选择,以满足不同应用程序的需求。通过使用第三方中间件,你可以轻松地扩展 Express 应用程序的功能,提高开发效率,并加强应用程序的安全性和性能。 中间件(Middleware)是 Express.js 框架中的关键概念之一,它允许你在请求和响应之间执行一系列操作,以实现各种功能和处理不同方面的逻辑。中间件对于一个web服务程序至关重要,希望大家好好理解。

模板引擎

模板引擎在web开发中具有许多优势,使其成为构建动态内容的强大工具。它使开发人员能够创建动态、可维护和安全的 web 应用程序,同时提供了分离关注点的能力。一般我们在web服务下使用ejs模板引擎。

下面我们写一个demo来加深一下对模板引擎的理解。

src目录下新建一个文件夹命名为views,并在里面新增一个文件index.ejs。下面是项目的现在的目录结构。

bash 复制代码
$ find . -type d -name "node_modules" -prune -o -print | tree -a -I 'node_modules'

.
├── .DS_Store
├── package.json
├── public
│   └── index.html
├── src
│   ├── middleware
│   │   └── my-middleware.js
│   ├── server.js
│   └── views
│       └── index.ejs
└── yarn.lock

server.js设置设置 EJS 模板引擎。

js 复制代码
//src/server.js
+ server.set('view engine', 'ejs');
+ server.set('views', __dirname + '/views');

// 定义一个简单的路由
+ server.get('/template', (req, res) => {
+   const data = {
+     title: 'EJS Demo',
+     message: 'Hello, EJS!'
+   };
+   // 渲染 EJS 模板并将数据传递给模板
+   res.render('index', data);
+ });

改写下views下的index.ejs文件

js 复制代码
<!DOCTYPE html>
<html>

<head>
    <title>
        <%= title %>
    </title>
</head>

<body>
    <h1>
        <%= message %>
    </h1>
</body>

</html>

下载ejs模板包

bash 复制代码
$ yarn add ejs -S

执行脚本命令yarn dev,打开http://localhost:8080/template,你将看到以下视图:

总结

中间件是express的一个极为重要的概念,本文讲解了一些中间件的基础知识,包含如何使用和怎样自己开发一个中间件。希望大家能够好好消化理解一下。模板引擎在web是构建动态内容的强大工具。它使开发人员能够创建动态、可维护和安全的web应用程序。

一个来自底层的小白程序员,希望大家多多点赞收藏关注,你的鼓励是我前进的动力,谢谢大家。

完整代码

github地址

参考资料

相关推荐
陈随易1 小时前
农村程序员-关于小孩教育的思考
前端·后端·程序员
云深时现月1 小时前
jenkins使用cli发行uni-app到h5
前端·uni-app·jenkins
昨天今天明天好多天1 小时前
【Node.js]
前端·node.js
2401_857610031 小时前
深入探索React合成事件(SyntheticEvent):跨浏览器的事件处理利器
前端·javascript·react.js
雾散声声慢1 小时前
前端开发中怎么把链接转为二维码并展示?
前端
熊的猫1 小时前
DOM 规范 — MutationObserver 接口
前端·javascript·chrome·webpack·前端框架·node.js·ecmascript
天农学子1 小时前
Easyui ComboBox 数据加载完成之后过滤数据
前端·javascript·easyui
mez_Blog1 小时前
Vue之插槽(slot)
前端·javascript·vue.js·前端框架·插槽
爱睡D小猪2 小时前
vue文本高亮处理
前端·javascript·vue.js
开心工作室_kaic2 小时前
ssm102“魅力”繁峙宣传网站的设计与实现+vue(论文+源码)_kaic
前端·javascript·vue.js