【Node.js从基础到高级运用】十三、NodeJS中间件高级应用

在现代web开发中,Node.js因其高效和灵活性而备受青睐。其中,中间件的概念是构建高效Node.js应用的关键。在这篇博客文章中,我们将深入探讨Node.js中间件的高级应用,包括创建自定义中间件、使用第三方中间件等。我们将从基础讲起,逐步深入,旨在为读者提供全面而深入的指南。

中间件简介

在Node.js中,中间件是一个函数,它可以访问请求对象(req)、响应对象(res)和应用程序的请求/响应循环中的下一个中间件函数。这些函数可以执行以下任务:

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

如果当前中间件函数没有结束请求/响应循环,它必须调用next()方法将控制权传递给下一个中间件函数,否则请求将被挂起。

中间件的类型

在 Node.js 中,根据用途和功能的不同,中间件大体可以分为以下几类:

  • 应用级中间件: 通过使用 app.use()app.METHOD() 函数加载,并且可以执行任何代码、修改请求和响应对象、结束请求-响应循环、调用下一个中间件。

  • 路由级中间件: 和应用级中间件相似,但它绑定到一个实例上 express.Router()。

  • 错误处理中间件: 用来处理应用中发生的各种错误。这类中间件通常定义了四个参数 (err, req, res, next)

  • 内置中间件: Express 框架自带的中间件,例如 express.static,用于提供静态资源。

  • 第三方中间件: 由社区开发,需要通过 npm 安装,可以提供额外的功能,比如解析请求体、处理 cookie 等。

创建自定义中间件

自定义中间件是扩展Express应用功能的基石。它允许我们对进入的请求进行预处理、实施安全检查、处理日志等。

示例:追踪请求时间

// 追踪请求处理时间的中间件
function requestTimeLogger(req, res, next) {
    const start = Date.now(); // 请求开始时间
    res.on('finish', () => { // 响应结束时触发
        const duration = Date.now() - start; // 计算处理时长
        console.log(`${req.method} ${req.url} - ${duration}ms`); // 记录请求方法、URL和处理时长
    });
    next(); // 继续处理请求
}

app.use(requestTimeLogger); // 将中间件添加到应用中

运行结果:
结合之前学过的books demo完成测试:
在这里插入图片描述

使用express.static提供静态文件服务

const express = require('express');
app.use(express.static('public')); // `public`目录下的文件现在可以通过Web访问了

创建public文件夹,在该文件夹下创建index.html文件,打开本地服务默认为index页面

使用第三方中间件

第三方中间件大大简化了常见功能的实现,如体解析、Cookie处理等。

示例:使用cors中间件管理跨源请求

const cors = require('cors'); // 引入cors中间件

app.use(cors()); // 应用cors中间件,允许所有跨域请求

这行代码使得我们的Node.js应用可以接受跨域请求,极大地简化了配置过程

响应格式化中间件

// 响应格式化中间件
function responseFormatter(req, res, next) {
    res.apiResponse = (data, error = null) => {
        if (error) {
            res.status(500).json({ success: false, error }); // 发送错误响应
        } else {
            res.status(200).json({ success: true, data }); // 发送成功响应
        }
    };
    next();
}

app.use(responseFormatter);

中间件组合应用

请求时间记录和请求大小限制

这个例子中,我们将使用两个自定义中间件,一个用于记录请求时间,另一个用于限制请求体的大小。

const express = require('express');
const bodyParser = require('body-parser');

const app = express();

// 中间件1: 记录请求时间
app.use((req, res, next) => {
  req.requestTime = Date.now(); // 添加一个新属性来保存请求时间
  next(); // 调用下一个中间件
});

// 中间件2: 限制请求体的大小
app.use(bodyParser.json({ limit: '10kb' })); // 使用body-parser限制请求体大小为10kb

// 路由处理
app.get('/', (req, res) => {
  const responseText = `Requested at: ${req.requestTime}`; // 使用中间件1添加的属性
  res.send(responseText);
});
// 测试限制请求体大小
app.post('/', (req, res) => {
  res.send('Received your request!');
});
// 添加错误处理中间件来观察限制
app.use((err, req, res, next) => {
  if (err instanceof SyntaxError && err.status === 400 && 'body' in err) {
    // 当请求体过大时,body-parser会抛出一个错误
    return res.status(413).send({ message: 'Request entity too large' });
  }
  // 如果不是请求体大小的问题,继续传递错误
  next(err);
});
// 启动服务器
app.listen(3000, () => {
  console.log('Server is running on port 3000');
});

运行结果:
在这里插入图片描述

条件性中间件和资源压缩

在这个例子中,我们将创建一个条件性中间件,用于根据请求的路径决定是否压缩响应。我们还会使用compression中间件来进行资源压缩。

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

const app = express();

// 条件性中间件: 只有请求路径为"/compress"时才使用compression中间件
app.use((req, res, next) => {
  if (req.path === '/compress') {
    compression()(req, res, next); // 调用compression中间件
  } else {
    next(); // 不需要压缩,直接调用下一个中间件
  }
});

// 路由处理
app.get('/compress', (req, res) => {
  // 发送一个大文本,压缩后传输
  const largeText = '...'; // 假设这里是一个很大的文本内容
  res.send(largeText);
});

app.get('/no-compress', (req, res) => {
  // 发送一个文本,不进行压缩
  res.send('This response is not compressed.');
});

// 启动服务器
app.listen(3000, () => {
  console.log('Server is running on port 3000 with conditional compression');
});

我们要做的是向/compress路由发送一个GET请求,并观察返回的响应头信息。检查响应头中的Content-Encoding是否包含gzip,这表明响应已经被压缩。

运行结果:
响应数据太小,没有达到压缩的阈值:
在这里插入图片描述

足够大的数据响应:

// 路由处理
app.get('/compress', (req, res) => {
  // 发送一个大文本,压缩后传输
  const largeText = '这是一个重复的大文本字符串,用于生成足够大的响应体来触发压缩。'.repeat(1000);
  res.send(largeText);
});

在这里插入图片描述

API速率限制和缓存

在这个例子中,我们将使用express-rate-limit来限制API的调用速率,以及使用简单的内存缓存来存储响应数据,减少重复计算。

mcache.putmemory-cache库中的一个方法,用于将数据存储在内存中。它接受三个参数:
key(字符串):用于存储和检索缓存值的唯一标识符。
value(任意类型):要存储在缓存中的数据。
time(毫秒):缓存数据在内存中存储的时间。超过这个时间后,数据将从缓存中删除。

const express = require('express');
const rateLimit = require('express-rate-limit');
const mcache = require('memory-cache');

const app = express();

// API速率限制中间件
const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15分钟
  max: 100 // 在15分钟内每个IP最多100次请求
});

// 应用API速率限制中间件
app.use('/api', limiter);

// 缓存中间件
const cache = (duration) => {
  return (req, res, next) => {
    let key = '__express__' + req.originalUrl || req.url;
    let cachedBody = mcache.get(key);
    if (cachedBody) {
      res.send(cachedBody);
      return;
    } else {
      res.sendResponse = res.send;
      res.send = (body) => {
        mcache.put(key, body, duration * 1000);// 将响应体存储在缓存中,duration以秒为单位
        res.sendResponse(body);
      };
      next();
    }
  };
};

// 路由处理
app.get('/api/expensive-data', cache(30), (req, res) => {
  // 假设这里有一些计算成本很高的数据生成过程
  const expensiveData = 'Expensive Data';
  res.send(expensiveData);
});

// 启动服务器
app.listen(3000, () => {
  console.log('Server is running on port 3000 with rate limiting and caching');
});
限制API调用速率

为了测试例子3中限制API调用速率的功能,我们可以快速连续发送多个请求到服务器的/api路由。如果超过了限制的数量,服务器应该返回429状态码(Too Many Requests)。
使用curl命令来测试API速率限制:

for i in {1..101}; do curl -X GET http://localhost:3000/api/expensive-data; done

在这里插入图片描述
这个命令会连续发送101个GET请求到/api/expensive-data。由于我们设置了15分钟内每个IP最多100次请求,所以在第101次请求时,将会收到Too many requests, please try again later.

测试内存缓存

为了测试内存缓存的功能,我们可以发送两次请求到同一个路由,并观察响应时间。第一次请求会生成数据并将其缓存,而第二次请求应该会直接从缓存中获取数据,响应时间应该更短。

app.get('/api/cache-data', cache(30), (req, res) => {
  // 假设这里有一些计算成本很高的数据生成过程
  const largeText = '这是一个重复的大文本字符串,用于生成足够大的响应体来触发压缩。'.repeat(1000);
  res.send(largeText);
});

使用curl命令来测试内存缓存:

# 第一次请求
time curl -X GET http://localhost:3000/api/cache-data

# 等待几秒钟

# 第二次请求
time curl -X GET http://localhost:3000/api/cache-data

time命令会显示curl命令执行所需的时间。第二次请求应该比第一次快,因为它应该是从缓存中获取数据。
第一次:
在这里插入图片描述
第二次:
在这里插入图片描述

总结

在上述例子中,我们展示了如何使用Node.js中间件进行请求时间记录、请求大小限制、条件性资源压缩、API速率限制以及简单的内存缓存。这些中间件的组合运用能够帮助你构建更加健壮和高效的Node.js应用程序。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/467695.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CTF题型 Http请求走私总结Burp靶场例题

CTF题型 Http请求走私总结&靶场例题 文章目录 CTF题型 Http请求走私总结&靶场例题HTTP请求走私HTTP请求走私漏洞原理分析为什么用前端服务器漏洞原理界定标准界定长度 重要!!!实验环境前提POST数据包结构必要结构快速判断Http请求走私类型时间延迟CL-TETE-CL 练习例题C…

三 C#插入排序算法

简介 插入排序算法是一种简单、直观的排序算法,其原理是将一个待排序的元素逐个地插入到已经排好序的部分中。 插入排序实现原理 插入排序算法是一种简单、直观的排序算法,其原理是将一个待排序的元素逐个地插入到已经排好序的部分中。 具体实现步骤…

Java类的初始化顺序

请直接看原文: Java类的初始化顺序_java创建顺序-CSDN博客 -------------------------------------------------------------------------------------------------------------------------------- 对于静态变量、静态初始化块、变量、初始化块、构造器,它们的…

滴答拍摄影项目|基于Spring Boot框架+ Mysql+Java+ Tomcat的滴答拍摄影项目设计与实现(可运行源码+数据库+设计文档)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 2024年56套包含java,ssm,springboot的平台设计与实现项目系统开发资源(可…

centos创建并运行一个redis容器 并支持数据持久化

步骤 : 创建redis容器命令 docker run --name mr -p 6379:6379 -d redis redis-server --appendonly yes 进入容器 : docker exec -it mr bash 链接redis : redis-cli 查看数据 : keys * 存入一个数据 : set num 666 获取数据 : get num 退出客户端 : exit 再退…

elk收集k8s微服务日志

一、前言 使用filebeat自动发现收集k8s的pod日志,这里分别收集前端的nginx日志,还有后端的服务java日志,所有格式都是用json格式,建议还是需要让开发人员去输出java的日志为json,logstash分割java日志为json格式&#…

java实现word转pdf

引入依赖包 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.2.5.RELEASE</version></dependency><dependency><groupId…

jQuery+CSS3自动轮播焦点图特效源码

jQueryCSS3自动轮播焦点图特效源码&#xff0c;源码由HTMLCSSJS组成&#xff0c;双击html文件可以本地运行效果&#xff0c;也可以上传到服务器里面 下载地址 jQueryCSS3自动轮播焦点图特效源码

day03vue学习

day03 一、今日目标 1.生命周期 生命周期介绍生命周期的四个阶段生命周期钩子声明周期案例 2.综合案例-小黑记账清单 列表渲染添加/删除饼图渲染 3.工程化开发入门 工程化开发和脚手架项目运行流程组件化组件注册 4.综合案例-小兔仙首页 拆分模块-局部注册结构样式完善…

LeetCode链表hard 有思路?但写不出来?

给你链表的头节点 head &#xff0c;每 k 个节点一组进行翻转&#xff0c;请你返回修改后的链表。 k 是一个正整数&#xff0c;它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍&#xff0c;那么请将最后剩余的节点保持原有顺序。 你不能只是单纯的改变节点内部的值…

功能齐全的免费 IDE Visual Studio 2022 社区版

面向学生、开放源代码和单个开发人员的功能齐全的免费 IDE 下载地址 Visual Studio 2022 社区版 - 下载最新的免费版本 Visual Studio 2022 Community Edition – Download Latest Free Version 准备安装 选择需要安装的程序 安装进行中 使用C学习程序设计相关知识并培养编程…

改变input placeholder的样式 (适用于vue uniapp 中的input textarea)

如下控制 <textarea name"" placeholder"请输入您要反馈的问题&#xff0c;以便我们为您解决" placeholder-style"font-weight: 500;font-size: 27rpx;color: #999999;" id"" cols"30" rows"10"></text…

电话机器人语音识别用哪家更好精准度更高。

语音识别系统的选择取决于你的具体需求&#xff0c;包括但不限于识别精度、速度、易用性、价格等因素。以下是一些在语音识别领域表现较好的公司和产品&#xff1a; 科大讯飞&#xff1a;科大讯飞是中国最大的语音识别技术提供商之一&#xff0c;其语音识别技术被广泛应用于各…

诺视科技完成亿元Pre-A2轮融资,加速Micro-LED微显示芯片商业化落地

近日&#xff0c;Micro-LED微显示芯片研发商诺视科技&#xff08;苏州&#xff09;有限公司&#xff08;以下简称“诺视科技”&#xff09;宣布完成亿元Pre-A2轮融资&#xff0c;本轮融资由力合资本领投&#xff0c;老股东盛景嘉成、汕韩基金以及九合创投持续加码&#xff0c;这…

Ubuntu 搭建gitlab服务器,及使用repo管理

一、GitLab安装与配置 GitLab 是一个用于仓库管理系统的开源项目&#xff0c;使用Git作为代码管理工具&#xff0c;并在此基础上搭建起来的Web服务。 1、安装Ubuntu系统&#xff08;这个教程很多&#xff0c;就不展开了&#xff09;。 2、安装gitlab社区版本&#xff0c;有需…

后端工程师快速使用vue和Element

文章目录 Vue1 Vue概述2 快速入门3 Vue指令3.1 v-bind和v-model3.2 v-on3.3 v-if和v-show3.4 v-for3.5 案例 4 生命周期 Element快速使用1 Element介绍2 快速入门3 当前页面中嵌套另一个页面案例代码案例截图 Vue 1 Vue概述 通过我们学习的htmlcssjs已经能够开发美观的页面了…

微服务技术栈SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式(五):分布式搜索 ES-下

文章目录 一、数据聚合1.1 聚合种类1.2 DSL实现聚合1.3 RestAPI实现聚合1.4 演示&#xff1a;多条件聚合 二、自动补全2.1 拼音分词器2.2 自定义分词器2.3 DSL自动补全查询2.5 实现酒店搜索框自动补全2.5.1 修改酒店索引库数据结构2.5.2 RestAPI实现自动补全查询2.5.3 实战 三、…

yocto编译测试

源码下载 git clone -b gatesgarth git://git.yoctoproject.org/poky lkmaolkmao-virtual-machine:~/yocto$ git clone -b gatesgarth git://git.yoctoproject.org/poky Cloning into poky... remote: Enumerating objects: 640690, done. remote: Counting objects: 100% (13…

C++开发基础——函数模板

一&#xff0c;函数模板 1.基础概念 模板编程是C中泛型编程的基础。 一个模板可以是创建类或者函数的蓝图。 模板编程分两种&#xff0c;分别是算法抽象的模板、数据抽象的模板。算法抽象的模板以函数模板为主&#xff0c;数据抽象的模板以类模板为主。 基于函数模板生成的…

matplotlib库简介及函数说明

目录 简介matplotlib.pyplot as plt 常用函数说明创建子图plt.subplots&#xff08;&#xff09;.plot&#xff08;&#xff09; 子图参数set_title&#xff08;&#xff09;axis2.legend()fig.autofmt_xdate() 简介 matplotlib 是一个用于创建二维图表和数据可视化的 Python …