NodeJS背后的人:Express👤

NodeJS背后的人:Express

本篇文章,学习记录于:尚硅谷🎢 文章简单学习总结:如有错误 大佬 👉点.

前置知识:需要掌握了解: JavaScript基础语法Node.JS环境API前端工程\模块化 ·····

早就听说NodeJS的强大,前端领域几乎无人不知、无人不晓,甚至在后端领域也有它的英姿👤

目前江湖中,web后端领域无疑是Java的天下,但: 作为一个Java开发不得不承认,它的内卷还有Spring的沉重...

对于一个小型项目来说,Java的各种框架|代码|包|开发时长|学习成本,稍微有亿点点大,那么,有没有一种更敏捷快速的开发呢》

这时:Node携手Express 出现了:首先Node本身就是JS运行环境,支持部署在服务器端,HTTP模块进行接口开发,集合Express即可更高效完成开发⛏️

Express

Express 是基于 Node.js 平台的极简、灵活的web 应用开发框架 -🔗🔗

简单来说:Express 是一个封装好的工具包,封装了很多功能,便于我们开发 WEB 应用:HTTP 服务)

Express使用🚀:

Express 本身是一个 npm 包,所以可以通过 npm 安装:

sh 复制代码
 npm init -y
 npm i express

Code目录下:edemo01.js 第一个Express Demo案例,和Node HTTP模块创建服务方式类似:

js 复制代码
 /** Express简单服务应用:*/
 //1.导入express模块
 const express = require('express');
 ​
 //2.创建应用对象
 const app = express();
 //3.定义服务路由: 
 app.get('/demo', (req,res)=>{ res.end('Hello ExpressServer'); })
 //4.监听端口、启动服务: 
 app.listen(5400, ()=>{ console.log('服务已经启动, 端口监听为 5400...'); });

ndoe edemo01.js 启动服务,最后--->浏览器请求: http://127.0.0.1:5400/demo 页面响应 Hello ExpressServer 测试成功!

Express路由🌐:

路由是网络通信中的一个核心概念:确保数据包能够以最有效的方式从源到达目的地;

Express路由: 确定了应用程序如何响应客户端对特定端点的请求,每个路由可以有一个或多个回调处理函数,当路由匹配时执行;

路由的组成: 端点是:URI/路径+特定的HTTP请求方法(GET\POST... 采用以下结构:app.METHOD(PATH, HANDLER)

  • appexpress 的一个实例:应用对象,由express()构造函数创建;
  • METHOD 指定HTTP的请求方法,常见的请求方式:GETPOSTPUTDELETE ···
  • (PATH,Handler)PATH定义匹配的路由路径,Handler 路由匹配时执行的回调函数:(请求对象req,响应对象res)=>{}
js 复制代码
 //多种路由规则: 
 //浏览器默认GET请求,其他请求可以使用接口工具进行测试;
 app.get('/getD', (req,res)=>{ res.end('常用于获取数据'); })
 app.put('/putD', (req,res)=>{ res.end('常用于更新数据'); })
 app.post('/postD', (req,res)=>{ res.end('常用于提交数据'); })
 app.delete('/deleteD', (req,res)=>{ res.end('常用于删除资源'); })

Express 特殊路由: app.get('/',(req,res)=>{}) 几乎所有的网站都必备该请求方式,get/通常表示:请求的根路径应用的入口;常用于网站主页

js 复制代码
 //处理根路径的 GET 请求
 app.get('/', (req, res) => { res.send('<H1>欢迎访问我的网站!</H1>'); });
  • 浏览器: 127.0.0.1:5400 即可直接请求响应!根路径的路由:允许你定义应用的默认页面或入口点

Express all全路由: app.all('xxx',(req,res)=>{}) 支持处理所有 HTTP 请求方法的路由处理器,只要请求路径匹配,即:路由所有的请求方式;

js 复制代码
 //all全路由请求:
 //处理所有请求方法的路由
 app.all('/allD', (req, res) => { res.end("无论是 GET、POST、PUT 还是其他任何请求方法,都会由这个路由处理器处理"); });
 ​
 //自定义 404 路由: *表示接受所有请求路径,通常定义在最后用于: 兜底404
 app.all('*', (req, res) => { res.end("<H1>404</H1>"); });
  • 路由的定义匹配规则: 建议:all全路由定义在最后位置 因为:Express 在处理请求时会按照路由定义的顺序进行匹配,找到第一个匹配的路由就停止;
  • 为避免干扰,相同请求路径|路由的操作被干扰,通常all定义在最后用于兜底操作;

获取请求报文参数:

原生Node 可以通过HTTP模块获取请求报文: 但对于一些参数获取存在一些不便:获取请求路径|参数|请求头...

Express对其进行了封装更方便获取请求报文中的数据:并兼容原生的HTTP模块获取方式:

js 复制代码
 //定义服务路由: 
 //假设请求 URL: http://127.0.0.1:5400/request?name=wsm&a=123
 app.get('/request', (req,res)=>{
     res.setHeader('content-type','text/html;charset=utf-8');    //解决中文乱码;
     //NodeJS原生获取请求报文:Express兼容原生
     console.log("获取请求URL: "+req.url);                        ///request?name=wsm
     console.log("获取请求方式: "+req.method);                     //GET
     console.log("获取请求版本号: "+req.httpVersion);              //1.1
 ​
     console.log("获取请求头对象: "+req.headers);                  //[obj,obj]
     console.log("获取请求头对象[接受语言]: "+req.headers['accept-language']);   
 ​
     //Express API获取请求报文:
     console.log("获取发请求设备IP: "+req.ip);                       //::ffff:127.0.0.1
     console.log("获取URL路径字符串: "+req.path);                    ///request
     console.log("获取URL查询字符串: "+JSON.stringify(req.query));   //{"name":"wsm","a":"123"}         
     console.log("获取请求头对象参数值: "+req.get("accept-language"));
     
     res.end('response succeed!!!'); }
 );

URL路由命名参数:

Express 路由中的命名参数: 是一种在 路由URL路径 中定义参数名称来捕获请求中特定部分的方法,

这允许你在路由处理器中访问这些参数的值,从而根据请求的不同条件执行不同的逻辑

语法: 在路由URL中:命名参数 进行定义, 回调函数通过req.param 获取命名参数值


举例: 某个商城的商品页面,可能根据不同的商品id,而展示不同的URL

  • https://127.0.0.1:5400/details/7654321.html
  • https://127.0.0.1:5400/details/1234567.html 虽然此处都是不同的路由,但仅需一个路由规则即可匹配,同时响应不同的页面;
js 复制代码
 //路由命名参数
 //:命名参数定义命名参数,并获取值;
 app.get("/details/:commodityID",(req,res)=>{
     //回调函数通过:req.params 获取命名参数的值;
     const {commodityID} = req.params;   //结构赋值获取结果;
     //if(判断商品ID.html是否存在返回页面)
     //else if(..其他操作..)
     //else{..404..} 
     res.end(`商品ID ${commodityID}`);
 });

上述路由定义: 所有的 https://127.0.0.1:5400/details/???? 都会进入该路由回调处理... 一定程度减少了代码开发,提高路由规则|灵活

路由命名参数注意事项:⚡⚡

  • 命名参数的名称是动态的,支持任何合法的 URL 字符串作为参数名、支持多命名定义:/XXX/:命名/:命名/XXX
  • 命名参数的值存储在 req.params 对象中,且与res.query 不冲突:/XXX/:命名/:命名/XXX?a=1&b=2 合法✅

路由命名 ≠ RESTFUL编码风格: 不小心经常搞混他们~ (((φ(◎ロ◎;)φ))) ~

  • Express 命名参数: 主要用于捕获URL中动态部分,提供一种灵活的路由处理方式;

  • RESTful 风格: 是一种更全面的 API 设计原则,包括统一的资源命名、清晰的HTTP方法使用等,强调简洁性、可伸缩性和易于理解性

    bash 复制代码
     - 相同的 路由路径,不同的请求方式,处理不同的结果
     - 获取所有用户:GET /users
     - 获取单个用户:GET /users/{userId}
     - 创建用户:POST /users
     - 更新用户:PUT /users/{userId}
     - 删除用户:DELETE /users/{userId}

获取请求体

Express 中获取请求体,需要使用中间件来解析请求体,不了解中间件:闪电学习 使用body-parser中间件来解析请求体:

body-parser 是Express 框架的一个中间件,用于解析HTTP请求体,使在处理 POST 请求时够方便地获取请求体中的数据;

Express 4.16.0 版本之后,body-parser 已经不再是 Express 的依赖模块,而是需要单独安装:

sh 复制代码
 npm install body-parser

安装了 body-parser,你就可以在 Express 应用程序中使用它:

js 复制代码
 /** Express获取请求体:*/
 //导入express模块|创建应用对象
 const express = require('express');
 const app = express();
 ​
 //导入body-parser模块;
 const bodyParser = require('body-parser');
 //根据模块创建中间件函数;
 let jsonParser = bodyParser.json();                         //处理JSON格式请求体的中间件函数
 let urlParser = bodyParser.urlencoded({extended:false});    //处理querystring格式请求体的中间件函数
                                                             //(建议)false------querystring解析 | true------三方库解析
 //定义路由 并绑定处理请求体的中间件:
 //解析JSON格式的请求体,将其转换为JavaScript对象------>至:request.body 
 app.post('/login', jsonParser, (request,response)=>{         //建议使用POST请求+接口测试工具进行测试设置请求
     console.log(request.body.username);
     console.log(request.body.userpass);
     response.send('获取请求体数据');
 });
 ​
 //解析表单:`application/x-www-form-urlencoded`请求体数据,转换为JavaScript对象------>至:request.body
 app.post('/login2', urlParser, (request,response)=>{
     console.log(request.body.username);
     console.log(request.body.userpass);
     response.send('获取请求体数据');
 });
 ​
 //监听端口、启动服务: 
 app.listen(5400, ()=>{ console.log('服务已经启动, 端口监听为 5400...'); });
  • POST login请求: http://127.0.0.1:5400/login 发送JSON请求:{"username":"wsm","userpass":"540"}
  • POST login2请求: http://127.0.0.1:5400/login2 解析表单application/x-www-form-urlencoded请求体数据,key-v匹配获取JavaScript对象;

关于 body-parser 模块使用/注意事项📑:

有的宝~为了方便,可以将上述的中间件,直接放进全局,这样在路由回调中就可以直接使用了;

body-parser 模块可用于创建多种请求规则解析请求体数据的中间件 ,包括:URL 编码JSON 数据以及多部分数据(比如文件上传 ...

  • 解析 URL 编码的请求体: 通过 bodyParser.urlencoded() 函数中间件,

    可以解析 application/x-www-form-urlencoded 格式的请求体数据,将其转换成 JavaScript 对象,方便在 Express 路由中进行处理

  • 解析 JSON 格式的请求体: 通过 bodyParser.json() 函数中间件,可以解析 JSON 格式的请求体数据,将其转换成 JavaScript 对象

  • 解析多部分数据(如文件上传: 通过 bodyParser.multipart() | bodyParser.raw() ,解析多部分数据格式 文件请求体数据

  • 处理文本格式的请求体数据: 通过 bodyParser.text() 函数,解析文本格式的请求体数据

文件上传☁️

Express 文件上传: 文件上传很多项目几乎都需要,也有很多中解决方案:body-parsermulterformidable(本次使用)

formidable 是另一个常用的处理文件上传的 Node 模块,它是一个功能强大,用于解析 multipart/form-data 类型的表单数据,包括文件上传;

sh 复制代码
 #安装 formidable 模块:
 npm install formidable

文件上传案例:

  1. 导入formidable模块:需解构赋值获得内部对象;
  2. 表单请求的路由定义中:通过formidable模块对象,创建对应的表单对象进行解析表单参数;如果是文件需要设置:multiples: true
js 复制代码
 /** Express 文件上传:*/
 //导入express模块|创建应用对象
 const express = require('express');
 const app = express();
 ​
 //导入formidable模块;
 const {formidable} = require('formidable');
 ​
 //文件上传
 app.post('/unloadFile',(req,res)=>{
     //创建form表单对象
     const form = formidable({ multiples: true });     //表示接收的表单是带文件的;
     //使用表单对象解析请求报文
     form.parse(req,(err,fields,files)=>{
         //表单解析错误响应
         if (err) { return res.status(500).json({ error: err.message }); }
         console.log(fields);    //普通表单:{x:[y],x2:[y2]}
         console.log(files);     //文件表单类型,支持多文件上传: { Xxx:[PersistentFile{...}],Xxx:[PersistentFile{...}], }
         res.send("成功!!");
     })
 })
 ​
 //监听端口、启动服务: 
 app.listen(5400, ()=>{ console.log('服务已经启动, 端口监听为 5400...'); });
  • Postman 工具发送multipart/form-data类型请求: (支持多文件|属性同时上传
  • 表单对象解析 req请求对象: err:表单解析错误信息fields:普通表单类型的参数files:文件类型表单接收的参数对象

指定上传路径: 🆗上述代码外面可以通过formidable解析获取到表单文件对象,实际开发中就需要我们手动的保存文件至指定位置------通过FS模块;

而:formidable的好处可以,定义表单对象时对文件类型,指定默认服务器存储位置: 实现更方便的文件上传操作;

原始 FS模块保存文件路径: 这里宝贝需要注意,因为可能会有版本问题导致 Files参数中的属性名不一致,导致会有问题 本次3.5^版本;

js 复制代码
 //导入formidable模块;
 const {formidable} = require('formidable');
 const fs = require('fs');
 //文件上传
 app.post('/unloadFile',(req,res)=>{
     //创建form表单对象
     const form = formidable({ multiples: true });     //表示接收的表单是带文件的;
     //使用表单对象解析请求报文
     form.parse(req,(err,fields,files)=>{
         //表单解析错误响应
         if (err) { return res.status(500).json({ error: err.message }); }
         console.log(fields);    //普通表单:{x:[y],x2:[y2]}
         console.log(files);     //文件表单类型,支持多文件上传: { Xxx:[PersistentFile{...}],Xxx:[PersistentFile{...}], }
 ​
         //使用FS模块保留文件上传:
         const img = files.img[0];    //获取files对象中文件
         const filePath = img.filepath;  // 临时文件路径:表单解析上传的文件会有一个默认存放路径;
         const newFilePath = __dirname+'/images/'+img.originalFilename; //定义设置拼接保存的新文件路径 name表示文件名;
     
         fs.rename(filePath, newFilePath, (err) => {             //FS将临时路径文件------转存至------新文件路径地址;
             if (err) { return res.status(500).json({ error: err.message }); }
             res.status(200).json({ message: 'File uploaded successfully' });
         });
     })
 })

formidable 表单配置设置上传路径: 3.5^版本,使用前要确保文件夹存在;

js 复制代码
 //文件上传 formidable配置上传
 app.post('/unloadFile2',(req,res)=>{
     //创建form表单对象
     const form = formidable({ 
         multiples: true,
         keepExtensions: true,   //保持文件后缀
         uploadDir: __dirname + '/images', //设置上传文件的保存目录
     });
     //使用表单对象解析请求报文 甚至可以不进行解析直接保存文件上传;
     form.parse(req,(err,fields,files)=>{
         //表单解析错误响应
         if (err) { return res.status(500).json({ error: err.message }); }
         res.status(200).json({ message: '数据库保存文件路径...等后期信息;' });
     })
 })

对于文件,还有很多操作,文件解析... 这里仅仅简单介绍一下,如开发遇见还需要不断学习深入💪💪💪

获取响应报文参数:

和请求报文一样,Express 提供了更方面操作响应报文的API 且,兼容原生Node

js 复制代码
 //定义服务路由: 
 app.get('/response', (req,res)=>{
     //Node原生设置响应报文
     // res.setHeader('content-type','text/html;charset=utf-8');    //解决中文乱码;
     // res.statusCode = 200;
     // res.statusMessage = 'response success';
     // res.setHeader('wsm','540');
     // res.write('write');
     // res.end('会中文乱码');
 ​
     //Express API设置响应报文
     // res.status(200);
     // res.set('wsm','540');
     // res.send('中文响应不乱码');     //Express提供send响应默认设置:UTF-8;
 ​
     //Express 支持链式编程
     res.status(200).set("header","wsm540").send("send 默认UTF-8编码格式");
 })
  • res.write()、res.end 总是成对出现,且 必须调用res.end()结束请求,否则浏览器会一直处于处于请求状态 end() 不支持多行|默认中文乱码
  • res.write() 仅支持输出字符|Buffer类型,纯数值则报错,在结束浏览器响应请求之前,允许多次调用;
  • res.send() 只能被调用一次,因为它等同于res.write+res.end(),支持换行|多种内容格式的输出;

响应文件内容

Express 中,你可以使用 res.sendFile("文件路径") 方法可以向客户端发送文件

指定文件的路径,Express 将自动设置正确的 Content-Type 并发送文件内容给客户端,设置响应文件:resFile.html

html 复制代码
 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>Document</title>
     <style>
         /* CSS样式来将标题居中 */
         body {
           display: flex;
           justify-content: center;
           align-items: center;
           height: 100vh;
           margin: 0;
         }
       </style>
 </head>
 <body>
     <h1>Express 文件响应!!!</h1>
 </body>
 </html>
js 复制代码
 /** 特殊响应: */
 //Path系统路径模块
 const path = require('path');   
 //响应文件内容
 app.get('/resFile',(req,res)=>{
     //Express 使用 res.sendFile() 向客户端直接响应文件内容;
     res.sendFile(path.resolve()+"/resFile.html");       //支持使用 path.resolve 绝对路径拼接;
 })

浏览器请求: http://127.0.0.1:5400/resFile 则直接响应 resFile.html文件内容;

重定向响应

Express 中,你可以使用 res.redirect("重定向地址") 方法来发送重定向响应

方法会向客户端发送一个 HTTP 重定向状态码(默认是 302 Found)以及一个 Location 头部,告诉客户端去请求另一个 URL

js 复制代码
 //重定向响应
 app.get('/resRedirect',(req,res)=>{
     //Express 使用 res.redirect() 进行重定向响应;
     res.redirect("https://www.bilibili.com/");  //重定向至 B站
 })

重定向 和 转发: 说到重定向不得不提的就是转发Express并没有针对转发的API,不过可以通过三方API实现;简单介绍一下:

  • 重定向: 是一种服务器端行为,它会告诉客户端浏览器请求的资源已经移到了其他位置,需要重新发起一个新的请求去获取这个资源

    服务器会发送一个带有重定向状态码(通常是 3xx)的响应,并在响应头中包含一个新的URL地址,告诉浏览器去请求这个新的URL

    :浏览器会发送两次请求,URL会发生改变,重定向没有限制,任何web资源(包括网络上的web资源)

  • 转发: 转发是一种服务器内部的行为,客户端请求服务器------服务器内重新请求并响应结果传递给客户端,客户端无感中间过程接收响应;

    :浏览器仅发送一次请求,URL不会发生改变,转发只限制在当前web项目中,转发由于是同一个请求,所以request域不变

⚙️⚙️应用场景:

  • 重定向: 常用于将用户导航到不同的URL,比如用户登录后重定向到首页,或者在资源经常移动或删除前端无法固定地址的重定向页面;
  • 转发: 常用于在同一个程序内部不同组件之间传递请求和响应对象,比如在MVC架构中,控制器可以处理请求并将请求转发到对应的视图来渲染页面;

JSON响应

在 Express 中响应 JSON 数据非常简单,使用 res.json("{JSON:'字符串'}") 方法进行 JSON 格式的响应

方法会自动设置适当的 Content-Type:application/json; charset=utf-8,并将 JSON响应客户端

js 复制代码
 //JSON向响应
 app.get('/resJson',(req,res)=>{
     //Express 使用 res.redirect() 进行重定向响应;
     res.json({ wsm:540, age:18, });  //重定向至 B站
 })

下载响应🗂️

在 Express 中可以使用 res.download('文件路径','下载文件名',(异常回调函数)=>{...})

来发送文件下载响应,这个方法指定的文件作为附件发送给客户端,浏览器触发文件下载操作⬇️🗂️

js 复制代码
 //下载响应
 app.get('/resDownload',(req,res)=>{
     //Express 使用 res.redirect() 进行重定向响应;
     res.download(path.resolve()+"/resFile.html",'测试下载文件.html',(err)=>{
         if(err){
             console.error('File download failed:', err);    //文件下载异常!
             res.status(500).send('Internal Server Error');
         }else{ console.log('File download successful'); }   //文件下载成功!
     });
 })

Express 的中间件🚻

Express 的中间件本质是一个回调函数Middleware

主要目的是处理 HTTP 请求,对请求进行预处理、执行一些操作,将请求next 传递------》下一个中间件或路由处理程序:

中间件允许你以模块化的方式组织你的 Express 应用程序,将应用程序拆分成小的、可复用的部分,使得代码更加清晰和易于维护

  • 中间件类型: 全局中间件路由中间件静态资源中间件
  • 和路由的回调函数一样,也具有:requerstresponse 参数对象;
  • Express 中间件的主要作用: 处理公共 HTTP 请求和响应、执行一些操作,如身份验证、日志记录、数据解析等 ···

编程web领域很多语言都有中间件的概念可能叫法不同: Java-servlet的过滤器、Spring的拦截器 ··· 本质原理概念都一样;

全局中间件:

全局中间件是在 Express 应用程序中的,每个请求上执行的中间件: 这意味着它们会影响到应用程序中的每个路由;⬇️⬇️定义全局路由:

js 复制代码
 //1.定义全局中间件函数:
 let allMiddleware = function(request,response,next){
     //函数本质就是一个普通函数,三个参数:请求对象、响应对象、next指向下一个中间函数|路由回调;
     //函数内执行|过滤···每一个请求路由都会进入这里,最后:next(); 执行路由...
 }
 //2.绑定至Express全局对象
 app.use(allMiddleware);
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Demo 案例: 相信很多网站都有的日志需求,网站行记录每一次请求:时间、接口、IP... 保存日志;

js 复制代码
 //通过FS模块追加指定日志文件;
 const fs = require('fs');
 fs.appendFileSync(path.resolve(__dirname, './access.log'), `${url}  ${ip}\r\n`);
  • 实现这个需求就是,在路由回调中添加+追加日志代码, 但对于所有路由都需要这段代码实在太冗余了所以,可以通过 全局中间件 进行统一定义:
js 复制代码
 //导入express模块、创建应用对象
 const express = require('express');
 const app = express();
 ​
 /** 定义全局路由: */
 //1.定义全局中间件:
 const fs = require('fs');       //导入需要的模块
 const path = require('path');
 let allMiddleware = function(request,response,next){
     let {url, ip} = request;    //解构赋值获取参数
     fs.appendFileSync(path.resolve(__dirname, './access.log'), `${url}  ${ip}\r\n`);    //FS追加写入文件;
     //最后 next() 执行下一个中间件|路由回调函数;
     next();
 }
 //2.绑定Express全局对象: 支持箭头函数声明: app.use((req,res,next)=>{....});
 app.use(allMiddleware);
 app.use((req,res,next)=>{console.log("中间件1"); next(); });
 app.use((req,res,next)=>{console.log("中间件2"); next(); });
 ​
 //定义服务路由: 
 app.get('/demo', (req,res)=>{ res.end('Hello ExpressServer'); })
 app.get('/demo2', (req,res)=>{ res.end('Hello ExpressServer'); })
 ​
 //监听端口、启动服务: 
 app.listen(5400, ()=>{ console.log('服务已经启动, 端口监听为 5400...'); }); 
  • 上述代码,无论浏览器如何请求: http://127.0.0.1:5400 都会进行日志记录;

多全局中间件定义:

Express 允许定义多个中间件,并按声明顺序进行执行: 多中间件,本质和单中间件一样,中间件执行顺序: 与它们被添加到app.use(???)的顺序相同;

js 复制代码
 //程序自上而下: 先全局绑定的中间件先执行;
 app.use((req,res,next)=>{console.log("中间件1"); next(); });   
 app.use((req,res,next)=>{console.log("中间件2"); next(); });

路由中间件:

路由中间件是与特定路由相关联的中间件: http://127.0.0.1:5400/demo|1|2 进行请求;

js 复制代码
 //定义服务路由: 
 //与全局中间件不同,因为是针对部分路由,所以需要直接与路由进行绑定;
 app.get('/demo',(req,res,next)=>{
     console.log("路由中间件");
     next();
 },(req,res)=>{
     console.log("路由回调函数");
     res.end('Hello ExpressServer'); 
 })
 ​
 //一个中间件支持绑定多个:路由;
 //一个路由也支持绑定多个:路由中间件;
 let routefun1 = function(req,res,netx){ console.log("路由中间件1"); netx(); }
 let routefun2 = function(req,res,netx){ console.log("路由中间件2"); netx(); }
 ​
 app.get('/demo1',routefun1,routefun2,(req,res)=>{
     console.log("路由回调函数1");
     res.end('Hello ExpressServer 1'); 
 })
 ​
 app.get('/demo2',routefun1,routefun2,(req,res)=>{
     console.log("路由回调函数2");
     res.end('Hello ExpressServer 2'); 
 })

路由|全局中间件,本身其实都是普通的函数: (req,res,next)=>{···} 通过不同的方式进行绑定,即不同的效果;

  • 直接与路由进行绑定------路由中间件
  • 通过: app.use(???) 全局绑定------全局中间件

静态资源中间件:

静态资源中间件: 顾名思义,是Express 专门用来处理静态资源的中间件:图像、.css、.JS...

它允许你指定一个目录,该目录中的文件将被直接提供给客户端,而无需经过任何额外的处理,让 Express 自动为客户端提供这些文件

js 复制代码
 //代码很简单:
 //将 public 目录下的文件设置为静态资源
 app.use(express.static('public'));

此时如果你有一个名为 public 的目录,并且在该目录中有一个名为 image.jpg

客户端仅需要: http://localhost:???/image.jpg 即可获取这个文件,而不需要任何额外的路由处理程序; 支持设置多静态资源目录: ⬇️

js 复制代码
 app.use(express.static('静态资源目录1'));
 app.use(express.static('静态资源目录2'));     //如果两个目录中都存在相同名称的文件,则优先使用定义的目录为准;

静态资源中间件------⚠️⚠️注意事项:

  • 静态资源中间件专门为响应静态资源而产生!!
  • 对于 / 路径请求,如果配置了静态资源中间件 Express会默认响应:index.html
  • 如果静态资源与路由规则同时匹配,谁先匹配谁就响应:自上而下原则,声明靠前的响应;

中间件执行顺序:

  1. 浏览器发送请求: 静态资源请求|路由请求,无论如何都会先经过全局中间件------next()
  2. 判断请求是: 静态资源请求则直接匹配对应的资源响应,路由则寻对应路由响应
  3. 路由请求: next()------路由中间件------最后,进入路由回调;
  4. 最后: response 响应浏览器页面;

⚠️⚠️中间件的注意事项:

Express 代码严格遵循自上而下执行,即:全局中间件,建议定义在路由的前面 则,请求匹配先匹配到路由则不会执行 全局中间件!!!

中间件的Next() 指向下一个中间件|路由回调: 要确保代码中next() 函数的正确使用!!!

静态资源中间件: 默认对/ 请求匹配 /index.html 如与路由冲突,遵循先入为主原则!!!

Express 路由模块化🧊

模块化的概念: 想必大家都知道:将一个大的功能拆分多个小的模块,最后组合在一起,方便管理维护;

路由模块化: 实际开发中一个项目,会有很多路由,如果都定义在一个配置文件中,那么根本不敢想象,无敌的臃肿冗余、不方便维护;

  • 路由的模块化是一种良好的做法,它使得代码结构更清晰、易于维护,并且便于团队协作;
  • 可以通过将路由处理程序分解为单独的模块,然后在应用程序中引入和使用这些模块来实现路由的模块化

路由模块化Demo:

这里有的宝贝,可能会疑惑❓为什么是 use() 不是定义中间件的吗? 不要固化!use() 不仅仅是引入中间件;

实际上,app.use() 是一个非常通用的方法,它用于将中间件绑定到应用程序的路径上,以及将路由绑定到应用程序的路径上

中间件和路由实际上都可以被认为是一个可以处理请求的处理函数,按定义顺序绑定到,程序请求路径上:

所以: 使用Express 一定要注意代码的编写顺序~~,不然可能会有想不到的bug🐞🐞

module1.js 模块: 定义商品的API路由模块;

js 复制代码
 /** Express路由模块化:*/
 //1.导入express模块
 const express = require('express');
 ​
 //2.创建路由对象
 const router = express.Router();        //路由对象其实就相当于 小型的app应用对象 const app = express();
 //3.路由对象定义路由:
 router.get('/commodity', (req, res) => { res.send('查询商品'); })       //查询商品
 router.get('/commodityID', (req, res) => { res.send('查询固定商品'); }) //查询固定商品
 ​
 //4.对外暴露路由对象:
 module.exports = router;

module2.js 模块: 定义购物车的API路由模块;

js 复制代码
 //导入express模块
 const express = require('express');
 ​
 //创建路由对象
 const router = express.Router();
 //路由对象定义路由:
 router.get('/shopcart', (req, res) => { res.send('查询购物车'); })          //查询购物车
 router.get('/shopcartCom', (req, res) => { res.send('查询购物车商品'); })   //查询购物车商品
 ​
 //对外暴露路由对象:
 module.exports = router;

mainApp.js 程序主文件: 启动程序,仅需要 node mainApp.JS 一个文件即可运行所有定义的路由请求;

js 复制代码
 /** Express模块化主文件:*/
 //导入express模块|创建应用对象
 const express = require('express');
 const app = express();
 ​
 //1.引入子路由文件
 const commodity = require('./module1');
 const shopcart = require('./module2');
 //2.设置和使用中间件:
 app.use(commodity);
 app.use(shopcart);
 ​
 //监听端口、启动服务: 
 app.listen(5400, ()=>{ console.log('服务已经启动, 端口监听为 5400...'); });
  • 请求: http://127.0.0.1:5400/shopcart 查询购物车、http://127.0.0.1:5400/commodity 查询商品
  • 路由前缀: app.use('/前缀',路由模块); 对于导入路由模块,支持同时设置路由前缀,来区分路由请求;

路由模块化高级⏫:

高级Express应用程序的目录结构如下:

sh 复制代码
 project/
 |-- controllers/
 |   |-- userController.js
 |   |-- productController.js
 |-- routes/
 |   |-- userRoutes.js
 |   |-- productRoutes.js
 |-- app.js
 |-- package.json

controllers 目录:在 controllers 目录中存放各个路由的处理程序,每个控制器模块负责处理一个或多个路由的请求和响应逻辑

routes 目录:在 routes 目录中存放路由模块,每个路由模块负责将特定路径的请求路由到相应的控制器处理程序

app.js 文件:引入和使用路由模块,并将其与 Express 应用程序关联起来

相关推荐
why1515 小时前
腾讯(QQ浏览器)后端开发
开发语言·后端·golang
浪裡遊5 小时前
跨域问题(Cross-Origin Problem)
linux·前端·vue.js·后端·https·sprint
声声codeGrandMaster5 小时前
django之优化分页功能(利用参数共存及封装来实现)
数据库·后端·python·django
呼Lu噜6 小时前
WPF-遵循MVVM框架创建图表的显示【保姆级】
前端·后端·wpf
bing_1586 小时前
为什么选择 Spring Boot? 它是如何简化单个微服务的创建、配置和部署的?
spring boot·后端·微服务
学c真好玩6 小时前
Django创建的应用目录详细解释以及如何操作数据库自动创建表
后端·python·django
Asthenia04126 小时前
GenericObjectPool——重用你的对象
后端
Piper蛋窝6 小时前
Go 1.18 相比 Go 1.17 有哪些值得注意的改动?
后端
excel6 小时前
招幕技术人员
前端·javascript·后端
盖世英雄酱581367 小时前
什么是MCP
后端·程序员