node.js基础学习-express框架-路由及中间件(十)

news2024/12/26 22:45:22

一、前言

Express 是一个简洁、灵活的 Node.js Web 应用框架。它基于 Node.js 的内置 HTTP 模块构建,提供了一系列用于构建 Web 应用程序和 API 的功能,使开发者能够更高效地处理 HTTP 请求和响应,专注于业务逻辑的实现。
其特点包括简单易用、中间件机制丰富、路由系统灵活等。通过使用 Express,可以快速搭建服务器,处理不同类型的请求,如网页渲染、数据接口提供等多种功能。

二、路由

1. 基本的路由格式

一个基本的 Express 路由由 HTTP 方法(如app.getapp.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路径时,会依次执行func1func2func3这三个中间件,最后执行路由处理函数来发送响应。

2. 中间件之间的传值

中间件可以通过reqres对象在中间件之间传递值。例如:

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.nameres.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的请求使用GETPOST方法进行跨域访问。可以根据具体的业务场景调整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部分的路由。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AndroidStudio-常见界面控件

一、Button package com.example.review01import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.widget.Button import android.widget.TextViewclass Review01Activity : AppCompatActivity() {override fun onCreate(savedInstanceStat…

【SpringMVC】参数传递 重定向与转发 REST风格

文章目录 参数传递重定向与转发REST风格 参数传递 ModelAndView&#xff1a;包含视图信息和模型数据信息 public ModelAndView index1(){// 返回页面ModelAndView modelAndView new ModelAndView("视图名");// 或// ModelAndView modelAndView new ModelAndView(…

Vue网页屏保

Vue网页屏保 在vue项目中&#xff0c;如果项目长时间未操作需要弹出屏幕保护程序&#xff0c;以下为网页屏保效果&#xff0c;看板内容为连接的资源。 屏保组件 <template><div v-if"isActive" class"screensaver" click"disableScreens…

计算机网络复习5——运输层

运输层解决的是进程之间的逻辑通信问题 两个主机进行通信归根结底是两个主机中的应用程序互相通信&#xff0c;又称为“端到端的通信” 端口 运行在计算机中的进程是用进程标识符来标志的。但不同的操作系统标识进程的方法不统一&#xff0c;因特网重新以统一的方法对TCP/IP…

qtcanpool 知 10:包管理雏形

文章目录 前言痛点转机雏形实践后语 前言 曾听闻&#xff1a;C/Qt 没有包管理器&#xff0c;开发起来太不方便。这是一个有过 node.js 开发经验的人对 Qt 的吐槽。 确实&#xff0c;像 python、golang、node.js 这些编程语言都有包管理器&#xff0c;给用户带来了极佳的开发体…

ASP.NET Core 9.0 静态资产传递优化 (MapStaticAssets )

一、结论 &#x1f4a2;先看结论吧&#xff0c; MapStaticAssets 在大多数情况下可以替换 UseStaticFiles&#xff0c;它已针对为应用在生成和发布时了解的资产提供服务进行了优化。 如果应用服务来自其他位置&#xff08;如磁盘或嵌入资源&#xff09;的资产&#xff0c;则应…

LeetCode 力扣 热题 100道(十五)搜索插入位置(C++)

给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 代码如下所示&#xff1a; class Solution { public:int searchIns…

WPF+LibVLC开发播放器-音量控制和倍速控制

界面 界面上增加音量的控件和倍速控制控件 音量控制 主要也是一个Slider进度条控件来实现音量调节 我们这里设置默认的最大值为100&#xff0c;默认Value值也为100&#xff0c;默认声音开到最大 这里目前完全由前端控制音量调节&#xff0c;可以直接使用ValueChanged事件实…

Vue3技术开发,使用纯CSS3动手制作一个3D环绕的相册展示效果,支持传入任意图片.3D轮播相册的组件

主要讲述封装一个3D轮播相册的组件&#xff0c;效果图如下&#xff0c;仅仅传入一个图片的数组即可&#xff0c;效果如下&#xff1a; 使用Vue3技术开发&#xff0c;支持传入任意张数的图片。 使用方法 <template><Swiper :list"list" /> </templat…

python怎么将字母大写

Python中有三种将字母转换为大写的方法&#xff1a;upper()、capitalize()、title()。 下面通过实例给大家介绍具体用法&#xff1a; str "www.php.com" print(str.upper()) # 把所有字符中的小写字母转换成大写字母 print(str.lower()) # 把所有字…

将vscode上的项目提交到github上

1.windows终端中 创建github仓库 创建完成 提交代码 git init git config --global user.email "fuyulai2024163.com" git config --global user.name "Fuyulai-Hub" git add . git commit -m "first commit" git remote add origin https://g…

【期末JavaEE项目】springboot+vue3完成中国铁路12306网站的业务实现【原创】

&#x1f939;‍♀️潜意识Java&#xff1a;个人主页 &#x1f399;告诉你&#xff1a;Java是世界上最美好的语言 &#x1f48e;比较擅长的领域&#xff1a;前端开发 是的&#xff0c;我需要您的&#xff1a; &#x1f9e1;点赞❤️关注&#x1f499;收藏&#x1f49b; 是…

浅谈CI持续集成

1.什么是持续集成 持续集成&#xff08;Continuous Integration&#xff09;&#xff08;CI&#xff09;是一种软件开发实践&#xff0c;团队成员频繁地将他们的工作成果集成到一起(通常每人每天至少提交一次&#xff0c;这样每天就会有多次集成)&#xff0c;并且在每次提交后…

电子商务人工智能指南 1/6 - 搜索、广告和发现

介绍 81% 的零售业高管表示&#xff0c; AI 至少在其组织中发挥了中等至完全的作用。然而&#xff0c;78% 的受访零售业高管表示&#xff0c;很难跟上不断发展的 AI 格局。 近年来&#xff0c;电子商务团队加快了适应新客户偏好和创造卓越数字购物体验的需求。采用 AI 不再是一…

【Git教程 之 版本控制】

Git教程 之 版本控制 Git教程 之 版本控制版本控制版本控制类型单用户版本控制系统&#xff08;VCS&#xff09;单用户版本控制系统&#xff08;VCS&#xff09;特点常见的单用户版本控制系统&#xff08;VCS&#xff09; 集中式版本控制系统&#xff08;CVCS&#xff09;集中式…

.NET Framework修复工具

某些精简Windows系统或者第三方下载的改版Windows系统在安装.NET Framework的时候会出现类似下面的错误信息: 可以使用微软官方出的.NET Framework修复工具解决, 下载地址: 【免费】.NETFramework修复工具资源-CSDN文库 下载后运行即可修复系统里的.NET Framework

计算机毕业设计Python轨道交通客流预测分析可视化 智慧交通 机器学习 深度学习 人工智能 爬虫 交通大数据

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

从计算服务器角度看智算与算力

计算服务器于智算和算力领域的地位堪称中流砥柱。人工智能旨在赋予计算机人类智能的使命&#xff0c;对计算服务器提出了近乎苛刻的要求。 在智算层面&#xff0c;它是计算服务器通向高效能的核心动力。凭借 CPU 与 GPU、FPGA、ASIC 等构建的异构组合&#xff0c;计算服务器可…

Lighthouse(灯塔)—— Chrome 浏览器性能测试工具

1.认识 Lighthouse Lighthouse 是 Google 开发的一款开源性能测试工具&#xff0c;用于分析网页或 Web 应用的性能、可访问性、最佳实践、安全性以及 SEO 等关键指标。开发人员可以通过 Lighthouse 快速了解网页的性能瓶颈&#xff0c;并基于优化建议进行改进。 核心功能&…

Logistic Regression(逻辑回归)、Maximum Likelihood Estimatio(最大似然估计)

Logistic Regression&#xff08;逻辑回归&#xff09;、Maximum Likelihood Estimatio&#xff08;最大似然估计&#xff09; 逻辑回归&#xff08;Logistic Regression&#xff0c;LR&#xff09;逻辑回归的基本思想逻辑回归模型逻辑回归的目标最大似然估计优化方法 逻辑回归…