Node JS 中间件如何工作?

每日前端夜话 第274篇

翻译: 疯狂的技术宅

作者:thirdrock team

来源:softnami

正文共:1999  字

预计阅读时间:10分钟

NodeJS development

什么是 Express 中间件?

  • 中间件在字面上的意思是你在软件的一层和另一层中间放置的任何东西。

  • Express 中间件是在对 Express 服务器请求的生命周期内所执行的函数。

  • 每个中间件都可以访问其被附加到的所有路由的 HTTP 请求和响应。

  • 另外,中间件可以终止 HTTP 请求,也可以用 next 将其传递给另一个中间件函数。中间件的这种“链”使你可以对代码进行划分并创建可重用的中间件。

编写 Express 中间件的要求

你需要安装一些东西来创建、使用和测试 Express 中间件。首先需要 Node 和 NPM。为确保已经安装,可以运行:

1npm -v && node -v

你应该看到已安装的 Node 和 NPM 版本。如果出现错误,则需要安装 Node。所有例子都应在 Node ver 8+ 和NPM ver 5+ 下使用。

本文使用了 Express 4.x 版。这很重要,因为从 3.x 版到 4.x 版有重大的更改。

Express中间件:基础

首先我们使用 Express 最基本的内置中间件。创建一个新项目并 npm 初始化它…

 1npm init
 2npm install express --save
 3
 4Create server.js and paste the following code:
 5
 6const express = require('express');
 7const app = express();
 8
 9app.get('/', (req, res, next) => {
10  res.send('Welcome Home');
11});
12
13app.listen(3000);

中间件解决什么问题?为什么要用它?

假设你 在 web 网络服务器上正在使用 Node.js 和 Express 运行Web应用程序。在此应用中,你需要登录的某些页面。

当 Web 服务器收到数据请求时,Express 将为你提供一个请求对象,其中包含有关用户及其所请求数据的信息。Express 还使你可以访问响应对象,可以在Web服务器响应用户之前对其进行修改。这些对象通常缩短为 reqres

中间件函数是使用相关信息修改 reqres 对象的理想场所。例如用户登录后,你可以从数据库中获取其用户详细信息,然后将这些详细信息存储在 res.user 中。

中间件函数是什么样的?

 1async function userMiddleware (req, res, next) {
 2    try {
 3        const userData = await getUserData(req.params.id);  //see app.get below
 4
 5        if(userData) {
 6                req.user = userData;
 7                next();
 8        }
 9    } catch(error)  {
10        res.status(500).send(error.message); //replace with proper error handling
11    }
12}

如果出现错误,并且你不想执行其他代码,则不要调用该函数。请记住在这种情况下要发送响应,否则客户端将会等待响应直到超时。

1var app = express();
2
3//your normal route Handlers
4app.get('/user/:id', userMiddleware, userController);

中间件链

你可以在中间件数组中或着通过使用多个 app.use 调用来链接中间件:

1app.use(middlewareA);
2app.use(middlewareB);
3app.get('/', [middlewareC, middlewareD], handler);

Express 收到请求后,与请求相匹配的每个中间件都将会按照初始化的顺序运行,直到有终止操作为止。

NodeJS development

因此,如果发生错误,则将按顺序调用所有用于处理错误的中间件,直到其中一个不再调用 next() 函数调用为止。

Express中间件的类型

  • 路由器级中间件,例如:router.use

  • 内置中间件,例如:express.static,express.json,express.urlencoded

  • 错误处理中间件,例如:app.use(err,req,res,next)

  • 第三方中间件,例如:bodyparser、cookieparser

  • 路由器级中间件

  • express.Router 使用 express.Router 类创建模块化的、可安装的路由处理。路由实例是一个完整的中间件和路由系统。

  • 你可以用中间件进行日志记录、身份验证等操作。如下所示,以记录用户的最新活动并解析身份验证标头,用它确定当前登录的用户并将其添加到 Request 对象。

  • 该函数在程序每次收到请求时执行。如果有错误,它会仅结束响应,而不会调用后续的中间件或路由处理。

 1var router = express.Router()
 2Load router-level middleware by using the router.use() and router.METHOD() functions.
 3The following example creates a router as a module, loads a middleware function in it, defines some routes, and mounts the router module on a path in the main app.
 4var express = require(‘express’);
 5var router = express.Router();
 6
 7// a middleware function with no mount path. This code is executed for every request to the router
 8// logging
 9async function logMiddleware (req, res, next) {
10    try {
11         console.log(req.user.id, new Date());
12     next();
13    } catch() {
14        res.status(500).send(error.message);
15    }
16}
17// authentication
18    async function checkAuthentication(req, res, next) => {
19// check header or url parameters or post parameters for token
20const token = req.body.token || req.query.token || req.headers['x-access-token'] || req.headers['authorization'];
21      if (token) {
22        try {
23            // verifies secret
24            req.decoded = await jwt.verify(token, config.secret)
25
26            let checkUser = await authenticateTokenHelper.getUserDetail(req);
27
28            // if everything is good, save to request for use in other routes
29                if (checkUser) {
30                        req.user = req.decoded
31                        next()
32                } else {
33                    return res.status(403).json({ 
34                    message: responseMessage.noAuthorized 
35                    })
36                }
37        } catch (err) {
38            return res.status(401).json({ message: responseMessage.invalidToken })
39        }
40  } else {
41    // if there is no token
42    return res.status(400).json({ message: responseMessage.invalidRequest })
43  }
44}
45router.use(logMiddleware);
46    router.get('/user, checkAuthentication, handler);
47

内置中间件

Express 有以下内置的中间件功能:

  • express.static 提供静态资源,例如 HTML 文件,图像等。

  • express.json 负载解析用 JSON 传入的请求。

  • express.urlencoded 解析传入的用 URL 编码的有效载荷请求。

错误处理中间件

错误处理中间件始终采用四个参数 (err,req,res,next) 。你必须通过提供四个参数来将其标识为错误处理中间件函数。即使你不需要使用 next 对象,也必须指定。否则 next 对象将被解释为常规中间件,并将会无法处理错误。基本签名如下所示:

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

例1:

1app.get('/users', (req, res, next) => {
2  next(new Error('I am passing you an error!'));
3});
4app.use((err, req, res, next) => {
5  console.log(err);    
6  if(!res.headersSent){
7    res.status(500).send(err.message);
8  }
9});

在这种情况下,管道末端的错误处理中间件将会处理该错误。你可能还会注意到,我检查了 res.headersSent 属性。这只是检查响应是否已经将标头发送到客户端。如果还没有,它将向客户端发送 HTTP 500  状态和错误消息。

例2:

你还可以链接错误处理中间件。通常以不同的方式处理不同类型的错误:

 1app.get('/users, (req, res, next) => {
 2  let err = new Error('I couldn\'t find it.');
 3  err.httpStatusCode = 404;
 4  next(err);
 5});
 6
 7app.get('/user, (req, res, next) => {
 8  let err = new Error('I\'m sorry, you can\'t do that, Dave.');
 9  err.httpStatusCode = 304;
10  next(err);
11});
12
13app.use((err, req, res, next) => {
14   // handles not found errors
15  if (err.httpStatusCode === 404) {
16    res.status(400).render('NotFound');
17  }
18   // handles unauthorized errors 
19  else if(err.httpStatusCode === 304){
20    res.status(304).render('Unauthorized');
21  }
22    // catch all
23   else if (!res.headersSent) {
24     res.status(err.httpStatusCode || 500).render('UnknownError');
25  }
26  next(err);
27});
  • 在这种情况下,中间件检查是否抛出了 404(not found)错误。如果是,它将渲染 “NotFound” 模板页面,然后将错误传递到中间件中的下一项。

  • 下一个中间件检查是否抛出了 304(unauthorized)错误。如果是,它将渲染“Unauthorized”页面,并将错误传递到管道中的下一个中间件。

  • 最后,“catch all” 错误处理仅记录错误,如果未发送响应,它将发送错误的 httpStatusCode(如果未提供则发送 HTTP 500 状态)并渲染 “UnknownError” 模板。

第三方级别的中间件

在某些情况下,我们将向后端添加一些额外的功能。先安装 Node.js 模块获取所需的功能,然后在应用级别或路由器级别将其加载到你的应用中。

示例:当 body-parser 处理 Content-Type 请求标头时,所有中间件都将使用解析的正文填充 req.body 属性。

 1const express = require('express');
 2const bodyParser = require('body-parser');
 3const app = express();
 4app.use(bodyParser.urlencoded({extended:false}))
 5app.use(bodyParser.json())
 6app.post('/save',(req,res)=>{
 7    res.json({
 8        "status":true,
 9         "payload":req.body
10    })
11}
12app.listen(3000,(req,res)=>{
13    console.log('server running on port')
14})

总结

中间件功能是一种非常好的方式,可以对每个请求或针对特定路由的每个请求运行代码,并对请求或响应数据采取措施。中间件是现代 Web 服务器的重要组成部分,并且非常有用。

原文:https://www.thirdrocktechkno.com/blog/how-Node-JS-middleware-works/

2020年京程一灯全新课程体系即将推出,请保持关注。

愿你在新的一年里保持技术领先,有个好前程,愿你月薪30K。我们是认真的 !

往期精彩回顾

面向开发人员的十大 NodeJS 框架

JavaScript 类完整指南

讲给前端的正则表达式

WebAssembly 正式成为 Web 的第四种语言

2020 年 Node.js 将会有哪些新功能

2020 年 Web 开发展望

从 JavaScript、ES6、ES7 到 ES10,你学到哪儿了?

15个 Vue.js 高级面试题

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章