【Node.js】详细记录express路由和中间件以及qs中间件的使用

路由的概念

什么是路由
  • 广义上说,路由就是映射关系
  • 类似按键和按键导致的不同服务之间的映射关系
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)
})

在这里插入图片描述

扫描二维码关注公众号,回复: 14704835 查看本文章
  • 将自定义组件封装为组件
  • 封装文件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)
})

猜你喜欢

转载自blog.csdn.net/weixin_44899940/article/details/129164812