node.js基础学习-express框架-路由及中间件(十)
一、前言
Express 是一个简洁、灵活的 Node.js Web 应用框架。它基于 Node.js 的内置 HTTP 模块构建,提供了一系列用于构建 Web 应用程序和 API 的功能,使开发者能够更高效地处理 HTTP 请求和响应,专注于业务逻辑的实现。
其特点包括简单易用、中间件机制丰富、路由系统灵活等。通过使用 Express,可以快速搭建服务器,处理不同类型的请求,如网页渲染、数据接口提供等多种功能。
二、路由
1. 基本的路由格式
一个基本的 Express 路由由 HTTP 方法(如app.get、app.post等)、路径(如'/'、'/about'等)和一个或多个回调函数组成。例如:
app.get('/', (req, res) => {res.send('Hello World!');
});
这里app.get表示处理 HTTP GET 请求,'/'是请求路径,(req, res) => {... }是回调函数。req代表请求对象,包含了请求的相关信息,如请求头、请求参数等;res代表响应对象,用于发送响应给客户端,如res.send方法用于发送响应内容。
2. 路径匹配
-
静态路径匹配:如
app.get('/about', (req, res) => {... });,只有当请求路径完全匹配/about时,这个路由才会被触发。 -
动态路径匹配:可以使用参数来创建动态路由。例如,
app.get('/users/:id', (req, res) => { const id = req.params.id; res.send(User ${id}); });,:id是一个动态参数,当请求路径类似/users/1、/users/2等时,req.params.id可以获取到相应的参数值,用于根据不同的用户 ID 等动态信息返回不同的响应。 -
路径参数的多种匹配方式
-
可选参数:在路径中使用
?来表示可选部分。例如app.get('ab?cd', (req, res) => {... });可以匹配ab或者abcd这样的路径。 -
通配符匹配:使用
*来表示匹配任意字符序列。例如app.get('ab*cd', (req, res) => {... });可以匹配ab后面跟着任意字符再接着cd的路径,如abxyzcd等。 -
正则表达式匹配:可以直接在路径中使用正则表达式。例如
app.get('/ab+cd', (req, res) => {... });匹配ab后面至少有一个字符再接着cd的路径,符合正则表达式ab+cd的模式。
3. 路由方法(HTTP方法)
Express 支持多种 HTTP 方法来定义路由,常见的有:
- GET 请求:用于从服务器获取数据。例如,获取网页内容、查询用户信息等场景。如
app.get('/books', (req, res) => { // 查询书籍信息并返回 });。 - POST 请求:通常用于向服务器提交数据,如提交表单数据、上传文件等。例如,
app.post('/login', (req, res) => { // 处理用户登录信息提交 });。 - PUT 请求:用于更新服务器上的数据。例如,更新用户信息、修改文章内容等场景。
app.put('/users/:id', (req, res) => { // 根据用户ID更新用户信息 });。 - DELETE 请求:用于删除服务器上的数据。比如删除用户记录、删除文件等。
app.delete('/products/:id', (req, res) => { // 根据产品ID删除产品信息 });。
三、 Express 中间件
中间件是在请求和响应周期中被调用的函数,它可以访问请求对象(req)、响应对象(res)和应用程序的请求 - 响应循环中的下一个中间件(next)。中间件可以执行各种任务,如日志记录、身份验证、数据预处理等,然后可以选择将请求传递给下一个中间件或者路由处理函数。
1. 中间件的使用方式
单个中间件:在路由处理函数中,可以有一个或多个中间件。例如:
app.get('/home', (req, res, next) => {console.log('This is a middleware');next();
}, (req, res) => {res.send('This is the home page');
});
这里第一个函数是中间件,它先打印一条日志,然后调用next()将控制权传递给下一个函数(这里是路由处理函数),用于发送响应。如果不调用next()函数,下一个中间件将不会被执行。
中间件数组:也可以将多个中间件组合成一个数组来使用。例如:
const func1 = (req, res, next) => {console.log('This is a middleware 1');next();
};
const func2 = (req, res, next) => {console.log('This is a middleware 2');next();
};
const func3 = (req, res, next) => {console.log('This is a middleware 3');next();
};
app.get('/list', [func1, func2, func3], (req, res) => {res.send('This is the list page');
});
当请求/list路径时,会依次执行func1、func2、func3这三个中间件,最后执行路由处理函数来发送响应。
2. 中间件之间的传值
中间件可以通过req或res对象在中间件之间传递值。例如:
const func4 = (req, res, next) => {req.name = 'John';res.age = 33;next();
};
const func5 = (req, res, next) => {const name = req.name;const age = res.age;res.send(`<h1>Hello ${name}, you are ${age} years old!</h1>`);
};
app.get('/hello', [func4, func5], (req, res) => {
});
在func4中间件中,通过req.name和res.age设置了值,然后在func5中间件中可以获取这些值来生成响应。
3. 不同类型的中间件
3.1 应用级中间件
通过app.use()方法来添加应用级中间件,它可以应用于整个应用程序或者特定的路径。如果没有指定路径,中间件会应用于所有的请求路径。例如:
const express = require('express');
const app = express();
app.use((req, res, next) => {console.log('This middleware is called for every request');next();
});
app.get('/hello', (req, res) => {res.send('Hello World');
});
app.listen(3000, () => {console.log('Server is running on port 3000');
});
在这个例子中,定义的中间件会在每个请求到达服务器时被调用,它先打印一条日志,然后通过next()函数将请求传递给下一个中间件或者路由处理函数。
3.2 特定路径的应用级中间件
可以指定中间件应用的路径,这样中间件只会对匹配该路径及其子路径的请求起作用。例如:
app.use('/admin', (req, res, next) => {console.log('This middleware is for /admin and its sub - paths');// 可以在这里进行权限验证等操作next();
});
app.get('/admin/dashboard', (req, res) => {res.send('Admin Dashboard');
});
当请求/admin路径或者以/admin开头的子路径(如/admin/dashboard)时,中间件会被调用。这对于对特定模块或功能进行统一的预处理(如权限验证)非常有用。
3.3 路由中间件
路由中间件和应用级中间件类似,也是通过app.use()在特定路由路径下注册,在路由处理函数之前执行,用于对该路由的请求进行预处理等操作。例如:
app.use('/api/users', (req, res, next) => {console.log('This is a route - level middleware for /api/users');// 可以在这里进行用户相关的预处理,如验证用户是否存在等next();
});
app.get('/api/users', (req, res) => {res.send('List of users');
});
这里的路由中间件会在处理/api/users路由的请求之前被调用,用于对用户相关的请求进行预处理。
3.4 错误处理中间件
错误处理中间件用于捕获和处理在路由处理函数或其他中间件中抛出的错误。它的函数签名与普通中间件略有不同,有四个参数(err, req, res, next),其中err参数用于接收错误对象。错误处理中间件应该放在所有其他中间件和路由定义之后,这样才能捕获它们抛出的错误。例如:
app.use((err, req, res, next) => {console.error(err.stack);res.status(500).send('Something went wrong!');
});
app.get('/error - route', (req, res, next) => {const error = new Error('This is a test error');next(error);
});
在这个例子中,当请求/error - route时,会在路由处理函数中创建一个错误对象并通过next(error)将错误传递给错误处理中间件。错误处理中间件会在控制台打印错误栈信息,然后向客户端返回一个状态码为500(服务器内部错误)的响应,消息为Something went wrong!。
3.5 内置中间件
**express.json():**用于解析application/json格式的请求体。在处理 POST 或 PUT 请求,且请求体数据为 JSON 格式时非常有用。例如:
app.use(express.json());
app.post('/data', (req, res) => {const data = req.body;// 处理接收到的JSON数据res.send('Data received');
});
当客户端发送一个application/json格式的 POST 请求到/data路径时,express.json()中间件会自动将请求体中的 JSON 数据解析为 JavaScript 对象,并挂载到req.body上,方便在路由处理函数中使用。
**express.urlencoded({ extended: false }):**用于解析application/x -www-form-urlencoded格式的请求体。通常用于处理 HTML 表单提交的数据。例如:
app.use(express.urlencoded({ extended: false }));
app.post('/form-data', (req, res) => {const formData = req.body;// 处理接收到的表单数据res.send('Form data received');
});
username=jun&password=123456这种格式的数据解析
3.6 第三方中间件
**morgan:**用于日志记录,它可以记录每个请求的详细信息,如请求方法、请求路径、响应状态码等。例如:
const morgan = require('morgan');
app.use(morgan('combined'));
这里morgan('combined')是一种日志格式选项,它会记录详细的请求信息。morgan还有其他日志格式,如'dev'(适合开发环境)、'common'等,开发者可以根据实际需求选择。
**cors:**用于解决跨域资源共享问题。例如:
const cors = require('cors');
app.use(cors({origin: 'http://example.com',methods: ['GET', 'POST'],
}));
这个配置允许来自http://example.com的请求使用GET和POST方法进行跨域访问。可以根据具体的业务场景调整origin(允许的源)、methods(允许的请求方法)等参数来满足跨域需求。
四、 模块化的路由中间件
express.Router是 Express 框架中的一个重要组件,它提供了一种模块化的方式来定义路由。使用Router可以将路由分组并封装到独立的模块中,这有助于组织大型应用程序的路由结构,使代码更加清晰、易于维护和扩展。
1. 创建和使用express.Router实例
创建实例:首先,需要创建一个Router实例。例如:
const express = require('express');
const router = express.Router();
定义路由:在Router实例上可以像在主app对象上一样定义各种 HTTP 方法的路由。例如:
router.get('/', (req, res) => {res.send('This is the root of the sub - router');
});
router.post('/data', (req, res) => {const data = req.body;res.send(`Received data: ${data}`);
});
挂载到主应用:创建并定义好Router的路由后,需要将其挂载到主 Express 应用上。例如:
const app = express();
app.use('/api', router);
这里将router挂载到/api路径下,这意味着router中定义的所有路由实际上是相对于/api路径的。例如,router中的'/'路由实际上对应的是/api/路径,'/data'路由对应的是/api/data路径。
2. 路由模块化的优势
- 代码结构清晰:通过将相关的路由分组到不同的
Router模块中,可以将一个大型应用的路由按照功能模块(如用户管理、产品管理、订单管理等)进行划分。例如,在一个电商应用中,可以有一个userRouter用于处理用户相关的路由(注册、登录、获取用户信息等),一个productRouter用于处理产品相关的路由(产品列表、产品详情、添加产品等),这样的代码结构更易于理解和维护。 - 复用性增强:
Router模块可以在不同的应用或者应用的不同部分复用。比如,一个通用的authRouter用于处理身份验证相关的路由(登录、验证 token 等),可以在多个不同的微服务或者应用模块中使用,只要它们遵循相同的接口和认证机制。 - 团队协作便利:在团队开发中,不同的开发人员可以负责不同的
Router模块,这样可以并行开发,减少代码冲突。例如,前端开发人员和后端开发人员可以分别开发与用户界面交互相关的路由和与数据库操作相关的路由,通过定义好的接口(如 API 路由)进行协作。
3. 中间件在express.Router中的使用
在Router级别使用中间件:可以在Router实例上使用中间件,这些中间件会应用到该Router所定义的所有路由上。例如:
const loggerMiddleware = (req, res, next) => {console.log(`Received a request for ${req.url}`);next();
};
router.use(loggerMiddleware);
这里定义了一个日志记录中间件loggerMiddleware,并通过router.use()将其应用到router上。这样,router中所有的路由在被访问时,都会先执行这个日志记录中间件。
在特定路由中使用中间件:也可以在Router的特定路由中使用中间件。例如:
const authMiddleware = (req, res, next) => {const token = req.headers.authorization;if (!token) {return res.status(401).send('Unauthorized: No token provided');}// 验证token的其他逻辑next();
};
router.get('/protected - route', authMiddleware, (req, res) => {res.send('This is a protected route');
});
在这个例子中,authMiddleware中间件只应用于/protected - route这个特定的路由。当访问该路由时,会先执行中间件进行身份验证,只有验证通过后才会执行路由处理函数。
4. 嵌套express.Router实例
express.Router实例可以进行嵌套,以创建更复杂的路由层次结构。这在构建具有多层级关系的 API 或者应用程序时非常有用。例如,在一个具有用户组和用户的应用中,可以先有一个groupRouter用于处理用户组相关的路由,在groupRouter内部再嵌套一个userRouter用于处理每个用户组内用户相关的路由。
const groupRouter = express.Router();
const userRouter = express.Router();
// 定义用户组相关的路由
groupRouter.get('/', (req, res) => {res.send('List of groups');
});
groupRouter.post('/', (req, res) => {res.send('Create a new group');
});
// 在用户组路由中嵌套用户路由
userRouter.get('/', (req, res) => {res.send('List of users in the group');
});
userRouter.post('/', (req, res) => {res.send('Add a new user to the group');
});
groupRouter.use('/:groupId/users', userRouter);
const app = express();
app.use('/groups', groupRouter);
在这个例子中,userRouter被嵌套在groupRouter内部。groupRouter处理用户组的基本路由,如获取用户组列表和创建新用户组。userRouter处理用户组内用户的相关路由,如获取用户组内用户列表和添加新用户到用户组。通过groupRouter.use('/:groupId/users', userRouter)将userRouter挂载到groupRouter的/:groupId/users路径下,这样就创建了一个嵌套的路由结构。当请求/groups/1/users(假设1是用户组 ID)时,会先由groupRouter处理/groups/1部分的路由,然后将请求传递给userRouter处理/users部分的路由。
相关文章:
node.js基础学习-express框架-路由及中间件(十)
一、前言 Express 是一个简洁、灵活的 Node.js Web 应用框架。它基于 Node.js 的内置 HTTP 模块构建,提供了一系列用于构建 Web 应用程序和 API 的功能,使开发者能够更高效地处理 HTTP 请求和响应,专注于业务逻辑的实现。 其特点包括简单易用…...
使用MSYS搭建linux开发环境踩坑笔记
前言: 使用linux系统或虚拟机进行嵌入式linux开发是常规方法; 使用MSYS是用于尝鲜和研究。 由于windows和linux的差异,使用MSYS代替Linux虚拟机会遇到很多坑。 主要原因在于: 1. windows和linux文件系统的差异:win不…...
vue3+ts+vite+ElementPlus上传进度条实时更新(UPLoad和progress)。
需求: 上传文件时,展示进度条实时更新: 下面是代码片段: <!-- 添加媒体弹窗 -- 上传 --><el-dialog v-model"centerDialogVisible" title"媒体信息" width"700" :close-on-click-modal"false&qu…...
AspNet WebAPI 模型绑定问题
继承System.Web.Http.ApiController的Action的Model如果被[Serializable]定义,会导致Model的字段无法绑定。 Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll [Serializable] public class Model {public string id { get; set; } }public MyA…...
Android 图形系统之七:SurfaceFlinger
一. 引言 什么是 SurfaceFlinger?SurfaceFlinger 的核心作用和地位?为什么需要了解 SurfaceFlinger? 二. SurfaceFlinger 的基本概念 Surface 和 SurfaceFlinger 的关系SurfaceFlinger 与图形渲染(OpenGL ES 和 Vulkan…...
14、鸿蒙学习——管理通知角标
针对未读的通知,系统提供了角标设置接口,将未读通知个数显示在桌面图标的右上角角标上。 通知增加时,角标上显示的未读通知个数需要增加。 通知被查看后,角标上显示的未读通知个数需要减少,没有未读通知时࿰…...
TongRDS分布式内存数据缓存中间件
命令 优势 支持高达10亿级的数据缓冲,内存优化管理,避免GC性能劣化。 高并发系统设计,可充分利用多CPU资源实现并行处理。 数据采用key-value多索引方式存储,字段类型和长度可配置。 支持多台服务并行运行,服务之间可互…...
[在线实验]-RabbitMQ镜像的下载与部署
镜像下载 docker的rabbitmq镜像资源-CSDN文库 加载镜像 docker load --input rabbitmq.tar 给镜像打标签 这里发现镜像名为none,需要给镜像重命名下 docker tag [镜像id] [新镜像名称]:[新镜像标签] docker tag ebaf409ffbe2 rabbitmq:management 运行镜像…...
Linux 系统文件描述符(File Descriptor)小白级介绍
1. 概述 Linux 遵循"一切皆文件"的理念。在 Linux 系统中,文件描述符是一个索引值(非负整数),指向内核为每个进程所维护的该进程打开文件的记录表。 如上所述,每个进程都维护着一张文件描述符表。 文件描述…...
【Verilog】实验二 数据选择器的设计与vivado集成开发环境
目录 一、实验目的 二、实验环境 三、实验任务 四、实验原理 五、实验步骤 top.v mux2_1.v 一、实验目的 1. 掌握数据选择器的工作原理和逻辑功能。 2. 熟悉vivado集成开发环境。 3. 熟悉vivado中进行开发设计的流程。 二、实验环境 1. 装有vivado的计算机。 2. Sw…...
IDL学习笔记(三)OMI数据处理。hdf5文件读取,图像反转,GeoTiff区别,月季年均值计算提取输出,单位转换,运行时间计算
modis Level 2 grid 数据是全球格网化数据。一天的数据全在其中。 modis Level 1 和 2 数据是一景一景的影像。 IDL学习笔记(三)OMI数据处理 hdf5文件读取单位转换,输出hdf5数据集的图像,并检查图像经纬度是否正确,若错…...
深入浅出:PHP中的数据类型全解析
文章目录 引言理解数据类型标量类型整数 (integer)浮点数 (float)布尔值 (boolean)字符串 (string) 复合类型数组 (array)对象 (object)资源 (resource)NULL 特殊类型Callable强制类型转换 实战案例总结与展望参考资料 引言 在编程的世界里,数据类型是构建任何应用…...
要使用 OpenResty 创建一个接口,返回客户端的 IP 地址,并以 JSON 格式输出
要使用 OpenResty 创建一个接口,返回客户端的 IP 地址,并以 JSON 格式输出 要使用 OpenResty 创建一个接口,返回客户端的 IP 地址,并以 JSON 格式输出方案一解决方案(openresty使用cjson)说明:使…...
智慧油客:从初识、再识OceanBase,到全栈上线
今天,我们邀请了智慧油客的研发总监黄普友,为我们讲述智慧油客与 OceanBase 初识、熟悉和结缘的故事。 智慧油客自2016年诞生以来,秉持新零售的思维,成功从过去二十年间以“以销售产品为中心”的传统思维模式,转向“以…...
ClickHouse守护进程
背景描述 维护CK过程中,有时候会有CK OOM,并且CK自己没有自动拉起的情况出现;那么这个时候就需要守护进程,最初我不说了Supervisor来做守护进程,但是当我手动kill的时候发现并没有自动拉起。 解决方案 于是乎自己写…...
智能合约
06-智能合约 0 啥是智能合约? 定义 智能合约,又称加密合约,在一定条件下可直接控制数字货币或资产在各方之间转移的一种计算机程序。 角色 区块链网络可视为一个分布式存储服务,因为它存储了所有交易和智能合约的状态 智能合约还…...
SQL面试题——拼多多SQL面试题 求连续段的起始位置和结束位置
拼多多SQL面试题 求连续段的起始位置和结束位置 今天的题目来自拼多多,我们先看一下题目描述 有一张表ids记录了id,id不重复,但是会存在间断,求出连续段的开始位置和结束位置 +---+ | id| +---+ | 1| | 2| | 3| | 5| | 6| | 8| | 10| | 12| | 13| | 14| | 15| +--…...
玩《三角洲行动》遇到游戏运行故障是什么原因?游戏运行故障要怎么解决?预防游戏运行故障问题出现
《三角洲行动》游戏运行故障解析与解决方案:原因、解决与预防 在畅游《三角洲行动》这款充满挑战与激情的游戏时,玩家可能会遭遇各种游戏运行故障,如卡顿、闪退、无法启动等问题。我将结合自己丰富的经验和知识,为大家深入剖析《…...
基于灰色神经网络的订单需求预测
灰色神经网络(Grey Neural Network, GNN) 是将灰色系统理论与人工神经网络相结合的一种模型,旨在处理不完全信息和小样本问题。灰色神经网络利用灰色系统的预测优势和神经网络的学习能力,能够在信息不完整或数据不充分的情况下实现…...
记录学习《手动学习深度学习》这本书的笔记(三)
这两天看完了第六章:卷积神经网络,巧的是最近上的专业选修课刚讲完卷积神经网络,什么卷积层池化层听得云里雾里的,这一章正好帮我讲解了基础的知识。 第六章:卷积神经网络 6.1 从全连接层到卷积 在之前的学习中&…...
从WWDC看苹果产品发展的规律
WWDC 是苹果公司一年一度面向全球开发者的盛会,其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具,对过去十年 WWDC 主题演讲内容进行了系统化分析,形成了这份…...
LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...
《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序
一、开发环境准备 工具安装: 下载安装DevEco Studio 4.0(支持HarmonyOS 5)配置HarmonyOS SDK 5.0确保Node.js版本≥14 项目初始化: ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...
NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合
在汽车智能化的汹涌浪潮中,车辆不再仅仅是传统的交通工具,而是逐步演变为高度智能的移动终端。这一转变的核心支撑,来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒(T-Box)方案:NXP S32K146 与…...
搭建DNS域名解析服务器(正向解析资源文件)
正向解析资源文件 1)准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2)服务端安装软件:bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能
1. 开发环境准备 安装DevEco Studio 3.1: 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK 项目配置: // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...
第7篇:中间件全链路监控与 SQL 性能分析实践
7.1 章节导读 在构建数据库中间件的过程中,可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中,必须做到: 🔍 追踪每一条 SQL 的生命周期(从入口到数据库执行)&#…...
tomcat指定使用的jdk版本
说明 有时候需要对tomcat配置指定的jdk版本号,此时,我们可以通过以下方式进行配置 设置方式 找到tomcat的bin目录中的setclasspath.bat。如果是linux系统则是setclasspath.sh set JAVA_HOMEC:\Program Files\Java\jdk8 set JRE_HOMEC:\Program Files…...
DBLP数据库是什么?
DBLP(Digital Bibliography & Library Project)Computer Science Bibliography是全球著名的计算机科学出版物的开放书目数据库。DBLP所收录的期刊和会议论文质量较高,数据库文献更新速度很快,很好地反映了国际计算机科学学术研…...
