【Node.js】详细记录express路由和中间件

news2024/11/18 7:47:17

Node.js

        • 路由的概念
          • 什么是路由
          • Express中的路由
          • 路由的匹配过程
        • 路由的使用
          • 创建路由模块文件
          • 注册路由模块文件
          • 为路由模块添加前缀
        • Express中间件
          • Express中间件格式
          • 定义中间件函数
          • 定义全局生效的中间件函数
          • 中间件的作用
          • 定义多个全局中间件
          • 局部生效的中间件
          • 定义多个局部生效的中间件
          • 了解中间件的使用注意事项
        • 中间件的分类
          • 应用级别的中间件
          • 路由级别的中间件
          • 错误级别的中间件
          • expressd的内置中间件
          • 第三方的中间件
        • 自定义中间件案例

路由的概念

什么是路由
  • 广义上说,路由就是映射关系
  • 类似按键和按键导致的不同服务之间的映射关系
Express中的路由
  • express中的路由指的是客户端的请求与服务器函数之间的映射关系
  • 由三部分组成
  • 请求的类型
  • 请求的URl地址
  • 处理函数
// 导入express
const express = require('express')
// 创建web服务器
const app = express()
// 启动文本服务器
app.listen(80, () => {
    console.log("80服务器启动http://127.0.0.1/")
    console.log("80服务器2启动http://127.0.0.1/")
})
//app.请求类型(请求地址,代表服务器的处理函数)
app.get('/',function(req,res){
    res.send("hello 路由")
})

在这里插入图片描述

路由的匹配过程
  • 每当一个请求到达服务器之后,需要先经过路由的匹配,只有匹配成功,才会调用处理函数
  • 在匹配时, 会按照路由的顺序进行匹配,如果请求类型和请求的URL同时匹配成功,express才会将请求交给对应的function函数进行处理

路由的使用

  • 最简单的是把路由挂载到app上,代码量多,不常用
//app.请求类型(请求地址,代表服务器的处理函数)
app.get('/',function(req,res){
    res.send("hello 路由")
})
app.post('/',function(req,res){
    res.send("hello 路由post")
})
创建路由模块文件
  • 模块化路由,将路由抽离为单独的模块
    – 步骤1:创建路由模块对应的js文件
    –步骤2:调用express.Router()函数创建路由对象
    –步骤3:向路由对象上挂载具体的路由
    –步骤4:使用module.exports向外共享路由对象
// 导入express
const express = require('express')
// 创建web服务器
const app = express()
// 创建路由对象
const router = express.Router()
router.get('/', function (req, res) {
    res.send("hello 路由")
})
router.post('/', function (req, res) {
    res.send("hello 路由post")
})
//向外共享路由对象
module.exports = router
注册路由模块文件
// 导入express
const express = require('express')
// 创建web服务器
const app = express()
// 导入路由
const userRouter = require('./router')
// 启动文本服务器
app.listen(80, () => {
    console.log("80服务器启动http://127.0.0.1/")
})
//使用app.use()注册路由模块,app.user()函数的作用,就是来注册全局中间件的
app.use(userRouter)

在这里插入图片描述

为路由模块添加前缀
//添加统一的访问前缀/api,此时想访问userRouter里的路由就得添加/api
app.use('/api',userRouter)

在这里插入图片描述

Express中间件

  • 类似污水处理的中间处理过程
  • express的中间件的调用流程
  • 当一个请求到达Express的服务器之后,可以连续调用多个中间件,从而对这次请求进行预处理
    在这里插入图片描述
Express中间件格式
  • 本质就是一个function的处理函数
  • 中间件函数的形参列表,必须包含next参数且放在最后,而路由处理函数只包含req和res
  • next函数的作用?
  • next函数是实现多个中间件连续调用的关键,它表示把流转关系转交给下一个中间件或路由
  • 也就是上图中的中间件1执行完调用next函数,到中间2等等
定义中间件函数
  • 在中间件的业务处理完毕后,必须调用next()函数,表示把流转关系交给下一个中间件或路由
//定义中间件函数
const nw = function (req, res, next) {
    console.log("中间件函数")
    next()
}
定义全局生效的中间件函数
  • 客户端发起的所有请求,到达服务器之后,都会触发的中间件,叫做全局生效的中间件
  • 通过调用app.use(中间件函数),即可定义一个全局生效的中间件
//定义中间件函数
const nw = function (req, res, next) {
    console.log("中间件函数")
    next()
}
//将nw注册为全局生效的中间件
app.use(nw)
//或者,组合使用注册
app.use(function (req, res, next) {
    console.log("中间件函数")
    next()
})
//或者,es语法组合使用
app.use((req, res, next)=> {
    console.log("中间件函数")
    next()
})

app.get('/1',function(req,res){
    res.send("hello 路由")
})

-执行,会在控制台打印中间件函数,next下一个组件,没有下一个组件就到了hello 路由
在这里插入图片描述

中间件的作用
  • 多个中间件,共享同一份req和res,基于这样的特性,我们可以在上游的中间件中,统一为req或者res对象添加自定义的属性或方法,供下游的中间件或者路由使用
//定义中间件函数
app.use((req, res, next) => {
    const time = Date.now()
    req.time = time
    console.log("中间件函数")
    next()
})
//下游的中间件或者路由也能用
app.get('/', function (req, res, next) {
    res.send("hello 路由get"+ req.time)
})
定义多个全局中间件
  • 可以使用app.use()连续定义多个全局中间件
  • 客户端请求到达服务器后,会按照中间件定义的先后顺序依次进行调用
//定义中间件函数
app.use((req, res, next) => {
    const time = Date.now()
    req.time = time
    console.log("中间件函数")
    next()
})
//定义中间件函数
app.use((req, res, next) => {
    const time = Date.now()
    req.time = time
    console.log("中间件函数2")
    next()
})
//定义中间件函数
app.use((req, res, next) => {
    const time = Date.now()
    req.time = time
    console.log("中间件函数3")
    next()
})
局部生效的中间件
  • 不使用app.use定义的中间件,只要在走指定路由才会执行中间件,先走中间件,再走路由
//定义中间件函数
const nw = function (req, res, next) {
    console.log("中间件函数")
    next()
}
//第二个参数就是中间件名称
app.get('/666',nw,function (req, res) {
    res.send("hello 路由get")
})
定义多个局部生效的中间件
const nw = function (req, res, next) {
    console.log("中间件函数")
    next()
}
const nw1 = function (req, res, next) {
    console.log("中间件函数2")
    next()
}
//下面两种方式完全等价,用哪种方式都行
//第二个参数就是中间件名称
app.get('/666',nw,nw1,function (req, res) {
    res.send("hello 路由get")
})
//第二个参数就是中间件名称
app.get('/666',[nw,nw1],function (req, res) {
    res.send("hello 路由get")
})
了解中间件的使用注意事项
  • 一定要在路由之前注册中间件,否则不生效
  • 客户端发送过来的请求,可以连续调用多个中间件进行处理
  • 执行完中间件的业务代码之后,不要忘记调用next()函数
  • 为了防止代码逻辑混乱,调用next()函数后不要再写额外的代码
  • 连续调用多个中间件时候,多个中间件之间,共享req和res对象

中间件的分类

  • 应用级别的中间件
  • 路由级别的中间件
  • 错误级别的中间件
  • Express内置的中间件
  • 第三方的中间件
应用级别的中间件
  • 通过app.use或者app.get()或者app.post(),绑定到app实例上的中间件
//全局中间件
app.use((req, res, next) => {
    const time = Date.now()
    req.time = time
    console.log("中间件函数")
    next()
})
//局部中间件
app.get('/666',nw,function (req, res) {
    res.send("hello 路由get")
})
路由级别的中间件
  • 绑定在express.Router()实例上的中间件
const express = require('express')
// 创建web服务器
const app = express()
// 导入路由
const router= require('./router')
router.use((req, res, next) => {
    const time = Date.now()
    req.time = time
    console.log("中间件函数")
    next()
})
// 启动文本服务器
app.listen(80, () => {
    console.log("80服务器启动http://127.0.0.1/")
})
//使用app.use()注册路由模块,app.user()函数的作用,就是来注册全局中间件的
app.use(router)
错误级别的中间件
  • 错误级别的中间件的作用:专门用来捕获整个项目中发生的异常错误,从而防止项目异常崩溃
  • 格式:
  • 错误级别的中间件的处理函数中,必须有四个参数,分别是(err,req,res,next)
  • 错误级别的中间件必须注册在所有路由之后
// 导入express
const express = require('express')
// 创建web服务器
const app = express()
// 启动文本服务器
app.listen(80, () => {
    console.log("80服务器启动http://127.0.0.1/")
})

//路由
app.get('/666',function (req, res) {
    throw new Error("抛出定义错误")
    res.send("hello get")
})
//会崩溃,所以接下来演示全局的中间件
//定义错误中间件函数
const nw = function (err,req, res, next) {
    res.send("检测定义错误1"+err.message)
    next()
}
app.use(nw)
expressd的内置中间件
  • 自4.16.0版本,Express内置了3个常用的中间件,提高了开发效率和体验
  • 1.express.static快速托管静态资源的内置中间件
  • 2.express.json解析json格式的请求体数据
  • 3.express.urlencoded解析URL-encoded格式的请求体数据(同理)
//路由
app.get('/',function (req, res) {
    // 在服务器可以使用req.body这个属性,来接受客户端发送过来的数据
    console.log(req.body)
    res.send("hello get")
})
  • 在服务器可以使用req.body这个属性,来接受客户端发送过来的数据
  • 默认情况下,如果不配置解析表单数据的中间件,则req.body默认等于undefined
    在这里插入图片描述
  • 解决方法:配置express.json中间件
  • 若还是空对象,检查postman自动生成的请求头勾上了吗(未复现)
app.use(express.json)
app.use(express.urlencoded({extended:false}))
app.get('/',function (req, res) {
    // 在服务器可以使用req.body这个属性,来接受客户端发送过来的数据
    console.log(req.body)
    res.send("hello get")
})
第三方的中间件
  • 由第三方开发出来的中间件,大家可以按需下载并配置第三方中间件
  • 运行npm install body-parser安装中间件
  • 运行require导入中间件
  • 调用app.use()注册使用中间件
  • 注意:Express内置的express.urlencoded()中间件,就是基于body-parser这个第三方中间件进一步封装的
//导入组件
const parser =require('body-parser')
//使用app.use()注册中间件
app.use(parser.urlencoded({extended:false}))
//路由
app.get('/',function (req, res) {
    // 在服务器可以使用req.body这个属性,来接受客户端发送过来的数据
    console.log(req.)
    res.send(req.query)
})

自定义中间件案例

手动模拟一个类似于express.urlerlencoded这样的中间件,来解析POST提交到服务器的表单数据

  • 定义中间件
  • 监听req的data事件
  • 监听req的end事件

如果数据量比较大,无法一次性发送完毕,客户端会把数据切割,分批发送到服务器,所以data事件可能会触发多次。每一次的触发,获取到的数据只是完整数据的一部分,需要手动进行拼接

  • 使用querystring模拟解析请求体数据

node.js内置了querystring模块,专门用来处理查询字符串,通过这个模块提供的parse()函数,将查询到的字符串解析为对象的格式

  • 将解析出来的数据挂载为req.body

因为上游的中间件和下游的中间件及路由之间,共享同一份req和res,因此,我们可以将解析出来的数据,挂载为req的自定义属性,命名为req.body,供下游使用

// 导入express
const express = require('express')
// 创建web服务器
const app = express()
// 启动文本服务器
app.listen(80, () => {
    console.log("80服务器启动http://127.0.0.1/")
})
//导入内置querystring模块
const qs = require('querystring')
//中间件的业务逻辑
app.use((req, res, next) => {
    let str = ""
    //监听req对象的data事件。客户端发送过来的新的请求体数据
    req.on('data', (chunk) => {
        //拼接请求体数据,格式转为字符串
        str += chunk
    })
    //当请求体数据接收完毕之后,会自动触发req的end事件
    req.on('end', () => {
        //可以拿到完整的请求体数据
        console.log(str)
        //字符串格式解析为对象格式
        const body = qs.parse(str) 
        //挂载为自定义属性req.body
        req.body=body
        next()
    })
})
//路由
app.post('/', function (req, res) {
    //下游使用挂载的req.body
    res.send(req.body)
})

在这里插入图片描述

  • 将自定义组件封装为组件
  • 封装文件parse.js
//导入内置querystring模块
const qs = require('querystring')
//中间件的业务逻辑
const bodyParse=(req, res, next) => {
    let str = ""
    //监听req对象的data事件。客户端发送过来的新的请求体数据
    req.on('data', (chunk) => {
        //拼接请求体数据,格式转为字符串
        str += chunk
    })
    //当请求体数据接收完毕之后,会自动触发req的end事件
    req.on('end', () => {
        //可以拿到完整的请求体数据
        console.log(str)
        //字符串格式解析为对象格式
        const body = qs.parse(str) 
        //挂载为自定义属性req.body
        req.body=body
        next()
    })
}
module.exports=bodyParse
  • 调用模块
// 导入express
const express = require('express')
// 创建web服务器
const app = express()
// 启动文本服务器
app.listen(80, () => {
    console.log("80服务器启动http://127.0.0.1/")
})
//导入自己封装的组件
const bodyParse=require('./parse')
app.use(bodyParse)
//路由
app.post('/', function (req, res) {
    //下游使用挂载的req.body
    res.send(req.body)
})

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

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

相关文章

Ae:使用代理

如果希望加快合成的预览或渲染速度,可考虑对素材使用代理 Proxy。虽然在 Ae 中,可以指定任何的静止图像或视频为代理,但一般情况下还是建议创建源素材的低分辨率版本来作为代理。对素材创建或指定代理后,可随意切换是否使用代理来…

物联网在医疗保健领域的5大创新应用

如今,物联网的发展越来越迅速,我们无法低估物联网在当今世界的重要性。大多数人每天都会使用到物联网设备。例如,当你使用智能手表来跟踪你的锻炼时,你就间接地使用了物联网的功能。由于物联网为世界带来了很多有效的帮助&#xf…

【涨薪技术】0到1学会性能测试 —— LR录制回放事务检查点

前言 上一次推文我们分享了性能测试分类和应用领域,今天带大家学习性能测试工作原理、事务、检查点!后续文章都会系统分享干货,带大家从0到1学会性能测试,另外还有教程等同步资料,文末免费获取~ 01、LR工作原理 通常…

【原创】java+swing+mysql生肖星座查询系统设计与实现

今天我们来开发一个比较有趣的系统,根据生日查询生肖星座,输入生日,系统根据这个日期自动计算出生肖和星座信息反馈到界面。我们还是使用javaswingmysql去实现这样的一个系统。 功能分析: 生肖星座查询系统,顾名思义…

【uniapp微信小程序】跨平台使用echarts的方案选择踩坑

一、前言 使用Uniapp(vue)开发微信小程序,想用echarts图表实现类似github热力图的效果。 简要列一些可行或不可行的方案。 二、方案对比 1. 【应用】:微信小程序原生开发 有echarts官网提供的跨平台方案:在微信小程…

CHATGPT是新的“搜索引擎终结者”吗?百度是否慌了

ChatGPT 以其非凡的自然语言处理 (NLP) 能力和清晰的响应风靡全球,有望带来一场重大的技术革命。在不知不觉中,叙事转向了ChatGPT与百度的对决,因为来自OpenAI的智能和健谈的聊天机器人已经慢慢获得了“潜在的百度终结…

Allegro如何快速删除孤立铜皮操作指导

Allegro如何快速删除孤立铜皮操作指导 在做PCB设计的时候,铺铜是常用的设计方式,在PCB设计完成之后,需要删除PCB上孤立的铜皮,即铜皮有网络但是却没有任何连接 如下图 通过Status报表也可以看到Isolated shapes 如何快速地删除孤立铜皮,具体操作如下 点击Shape

C++ sort()函数和priority_queue容器中比较函数的区别

普通的queue是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除。priority_queue中元素被赋予优先级。在创建的时候根据优先级进行了按照从大到小或者从小到大进行了自动排列(大顶堆or小顶堆)。可以以O(log n) 的效率查找…

23. 合并K个升序链表

解题思路:两种解法,一种优先级队列,一种分治优先级队列解法:以节点中存储的值进行排序依次遍历所有的链表,把链表中的节点加入到优先级队列中依次从优先级队列的弹出并删除最小的元素加入到新的链表中,直到…

final关键字是什么以及final的四种用法

final定义: final的翻译是最终,也就表示着它修饰的对象是最后一次被继承等含义。 被final修饰的对象不能更改其定义以及变量值 final的四种用法 一、修饰变量 public class Demo1 {public final int a 6;public void test() {a 10;} final修饰变量…

ESP-C3入门12. HTTPS请求、堆内存使用及JSON处理

ESP-C3入门12. HTTPS请求、堆内存使用及JSON处理一、创建HTTPS请求1. 基本流程2. ESP32 使用https证书的方式(1) 内置证书(2) ESP32管理证书3. 开发环境配置(1) 引用 esp-tls 库(2) 启用 CONFIG_MBEDTLS_CERTIFICATE_BUNDLE二、堆内存的使用1. 堆内存和栈内存2. 堆内存的使用(1…

Linux基础命令-which查找命令文件位置

文章目录 which 命令功能 语法格式 基本参数 参考实例 1)查找chmod命令的文件位置 2)查找chmod命令的所有路径 3)一次性查找多个命令路径 4)组合其他命令一起使用 5)显示命令的版本信息 命令总结 which 命…

11 udp 发送数据的流程梳理

前言 呵呵 之前曾经看到过 湖光大佬 的 tcp 的流程梳理 呵呵 很高深 有很多不明白的地方, 不光是涉及到 linux 网络处理本身的东西, 还涉及到了 tcp协议 的一些具体的实现, 是非常的复杂 这里之前 在 0voice/linux_kernel_wiki 上面看到了网络协议栈部分的梳理 呵呵 自己也…

计算机网络(七):DNS协议和原理,DNS为什么用UDP,网页解析的全过程

文章目录一、什么是DNS二、DNS的作用三、DNS作用四、DNS为什么用UDP五、如果打开一个网站很慢,要如何排查六、网页解析的全过程一、什么是DNS DNS是域名系统的英文缩写,是一种组织成域层次结构的计算机和网络服务命名系统,用于TCP/IP网络。 …

数据库行业的 “叛逆者”:大数据已“死”,MotherDuck 当立

“大数据”已死——现今我们最重要的事情不是担心数据大小,而是专注于我们将如何使用它来做出更好的决策。数据库行业发展至今,在数据层面有很多的加速和变革,尤其是过去几年的云数仓爆炸式增长,带来了行业的很多变化。毫无疑问&a…

Fiddler报文分析-断点应用、模拟网络限速-HTTPS的 拦截

目录 一、报文分析 Statistics 请求性能数据 检查器(Inspectors) 自定义响应(AutoResponder) Composer Composer的功能就是用来创建HTTP Request然后发送请求。 允许自定义请求发送到服务器,即可以手动创建一个新…

Acwing---1242. 修改数组——并查集的简单应用

修改数组1.题目2.基本思想3.代码实现1.题目 给定一个长度为 N 的数组 A[ A1,A2,⋅⋅⋅AN ] ,数组中有可能有重复出现的整数。 现在小明要按以下方法将其修改为没有重复整数的数组。 小明会依次修改 A2,A3,⋅⋅⋅,AN。 当修改 Ai 时,小明会检查 Ai 是…

在VScode里面添加Python解释器

VScode编辑器在安装好Python插件之后会自动选择环境变量中排序最高的那一个解释器作为默认解释器,而想要额外添加新的Python解释器就需要自己设置。 Python和VScode编辑器安装在Windows系统中 Python扩展插件安装在VScode编辑器 第一步,打开VScode编辑…

leaflet 绘制渐变折线(094)

第094个 点击查看专栏目录 本示例的目的是介绍如何在vue+leaflet中绘制渐变折线。 直接复制下面的 vue+openlayers源代码,操作2分钟即可运行实现效果. 文章目录 示例效果配置方式示例源代码(共89行)安装插件相关API参考:专栏目标示例效果 配置方式 1)查看基础设置:htt…

分布式算法 - Raft算法

Paxos是出了名的难懂,而Raft正是为了探索一种更易于理解的一致性算法而产生的。它的首要设计目的就是易于理解,所以在选主的冲突处理等方式上它都选择了非常简单明了的解决方案。推荐阅读提示强烈推荐通过如下资料学习raft。 raft.github.io这里面有一个…