Node.js 第四天 express路由,中间件
文章目录
Express路由
路由的概念
什么是路由
广义上来讲,路由就是映射关系
Express中的路由
在Express中,路由指的是客户端的请求和服务处理函数之间的映射关系
Express中的路由分3部分组成,分别是请求的类型 , 请求的URL地址 , 处理函数
Express路由实例
这个就是我们前面经常用的例子
//匹配GET请求,且请求URL为/ app.get('/',function(req,res){ res.send('Hello world') }) //匹配POST请求,且请求URL为/ app.post('/',function(req,res){ res.send('GOT a POST request') })
路由的匹配过程
每当一个请求到达服务器之后, 需要先经过路由的匹配 , 只有匹配成功后 , 才会调用对应的处理函数
在匹配时,按照路由的顺序进行匹配,如果请求的类型和URL同时匹配,则会执行对应的函数来处理这次的请求
就是说,我们可以根据不同的请求类型和不同的URL,定义多个app.get()
和app.post()
, 当收到一个请求时, 按照刚才定义的顺序去匹配
注意
- 按照定义的先后顺序进行匹配
- 请求类型和请求URL同时匹配成功,才会调用相应的处理函数
路由的使用
最简单的用法
也就是我们刚才说的那个示例代码
const express = require('express') //创建web服务器 const app = express() //挂载路由 app.get('/',(req,res)=>{res.send('hello world')}) app.post('/',(req,res)=>{res.send('别发了,我收到了')}) //启动web服务器 app.listen(80,()=>{ console.log('app is running at http://127.0.0.1') })
但是这种方法我们一般很少使用, 你想想 , 加入我的项目结构很复杂 , 那岂不是需要些很多的app.get()
和app.post()
, 整个代码的体量就会很大
模块化路由
为了方便对路由进行模块化的管理,Express不建议将路由直接挂载到app上 , 而是推荐将路由抽离为单位的模块 , 简单点说 , 就是我们把挂载路由的部分代码单独放到一个.js文件中
- 创建路由模块对应的.js文件
- 调用
express.Router()
函数创建路由对象 - 向路由对象上挂载具体的路由
- 调用
module.exports
向外共享这个路由对象 - 使用
app.use()
函数注册路由模块
创建路由模块
var express = require('express') var router = express.Router() router.get('/user',(req,res) => { res.send('Get User list') }) router.post('/user',(req,res) => { res.send('Post a request') }) module.exports = router
注册路由
在项目文件.js
引入路由模块
// 导入路由模块 const userRouter = require('./Router.js') // 使用app.use() 注册路由模块 app.use(userRouter)
这样访问http://127.0.0.1/user
为路由模块添加前缀
类似于前面所说的静态资源托管 , 路由模块也支持添加前缀的方式
//导入路由模块 const userRouter = require('./Router.js') //调用app.use()注册路由模块 , 并添加统一的访问前缀 /api app.use('/api',userRouter)
这样 , 当我们请求时,需要加上api
前缀,http://127.0.0.1/api/user
Express中间件
中间件的概念
什么是中间件
特指业务流程的中间处理环节
中间价一般都输输入和输出, 上一级的输出作为下一级的输入
举个现实生活中的例子
Express中间件的调用流程
当一个请求达到Express服务器之后, 可以连续调用多个中间件, 从而对这次请求进行预处理
Express中间件的格式
Express中间件的本质就是一个function处理函数
前面的路由函数的形参列表只包含req,res
两个参数,
在中间件函数的形参列表中, 必须包含next
参数
next函数的作用
next
函数是多个中间件连续调用的关键 , 他表示把流转关系转交给下一个中间件或路由
next
就好像是C语言中的一个指针 , 他指向下一个中间件或路由
Express中间件初体验
定义中间件函数
可以通过如下的方式, 定义一个最简单的中间件函数
//常量mw 所指向的,就是一个中间件函数 const mw = function(req, res ,next){ console.log('这是一个最简单的中间件函数') //注意, 在当前中间件的业务处理完毕后, 必须调用next()方法 //表示把流转关系转交给下一个中间件或路由 next(); }
全局生效的中间件
客户端发起的任何请求, 到达服务器之后 , 就会触发的中间件 ,叫做全局生效的中间件
通过调用app.use(中间件函数)
, 即可定义一个全局生效的中间件函数
//常量mw 所指向的,就是一个中间件函数 const mw = function(req, res ,next){ console.log('这是一个最简单的中间件函数') //注意, 在当前中间件的业务处理完毕后, 必须调用next()方法 //表示把流转关系转交给下一个中间件或路由 next(); } app.use(mw)
定义全局中间件的简化形式
//全局生效的中间件的简化形式 app.use(function(req,res,next){ console.log('这是一个中间件函数') next() })
中间件的作用
多个中间件之间 , **共享同一份req和res ** , 基于这样的特性 , 我们可以在上游的中间件中,统一为req或res对象添加自定义的属性或方法 , 供下游的中间件或路由进行使用
定义多个全局中间件
当我们定义了多个全局中间件时 , 程序会按照全局中间件的顺序来依次处理
const express = require('express') const app = express() //这是第一个全局中间件 app.use((req,res,next)=>{ console.log('经过第一次处理') next() }) //这是第二个全局中间件 app.use((req,res,next)=>{ console.log('经过第二次处理') next() }) app.get('/user',(req,res)=>{ res.send('User page') }) app.listen(80,()=>{ console.log('app is running') })
局部生效的中间件
不使用app.use()
定义的中间件 , 叫做局部生效的中间件
想要在哪个地方使用中间件函数, 直接在哪个地方调用即可
const express = require('express') const app = express() //定义一个中间件函数 const m1 = (req,res , next)=>{ console.log('我是一个局部生效的中间件') next() } //我们想要这个中间件函数只在第一个路由中生效 //只需要将这个中间件函数放在路由处理函数前面的参数位置即可 app.get('/',m1,(req,res)=>{ res.send('Home page') }) app.get('/user',(req,res)=>{ res.send('user page') }) app.listen(80,()=>{ console.log('app is running') })
定义多个局部中间件
可以在路由中, 通过如下两种等价的方式 , 使用多个局部中间件
//以下两种写法是完全等价的 app.get('/',m1,m2,(req,res)=>{res.send('HOme page')}) app.get('/',[m1,m2],(req,res)=>{res.send('HOMe Page')})
也是按照局部中间件的顺序去调用的
了解中间件的5个使用注意事项
- 一定要在路由之前注册中间件 , 因为整个程序是从前到后执行的 , 如果中间价在路由之后, 那么久直接响应给客户端了 , 中间件就没有用了
- 执行完中间件的处理后, 千万别忘记调用
next()
函数 - 调用完
next()
之后, 就不要再写多余的代码了 , 因为执行到next()
的时候,就已经转入下一个中间件或者路由了,next()
后面的代码就屁用没有了
中间件的分类
为了方便使用 , Express官方把常见的中间件用法 , 分成了5大类 , 分别是
- 应用级别的中间件
- 路由级别的中间件
- 错误级别的中间件
- Express 内置的中间件
- 第三方的中间件
应用级别的中间件
通过app.use()
或app.get()
或app.post()
, 绑定到app实例上的中间件 , 叫做应用级别的中间件
app.use((req,res,next)=>{ console.log('经过第一次处理') next() }) app.get('/',[m1,m2],(req,res)=>{ res.send('Home page') })
路由级别的中间件
绑定到express.Router()
实例上的中间件 , 叫做路由级别的中间件 , 他的用法和应用级别中间件相同.
只不过,应用级别的中间件是绑定到app实例上的 , 路由级别的中间件是绑定到router
实例上,
const router = express.Router() //路由级别的中间件 router.use(function(req,res,next){ console.log('到达路由级别中间件') next() }) app.use(router)
错误级别的中间件
作用: 专门用来捕获项目中发生的异常 , 从而防止项目异常崩溃
当项目中发生错误时 , 就会直接进入到错误级别的中间件 进行处理
格式 : 错误级别的中间件处理函数 , 必须有4个形参 , 且这四个形参的顺序不能乱,分别是(err,req,res,next)
app.get('/',(req,res)=>{ throw new Error('服务器内部发生错误') res.send('Home page') }) app.use((err,req,res,next)=>{ console.log('发生了错误:'+err.message) res.send('Error:'+err.message) })
项目发生错误后, 直接进入错误级别中间件
注意 : 错误级别中中间件 , 必须注册在所有路由之后 , 否则无效
Express内置的中间件
自express4.16.0版本之后 , Express内置了3个常用的中间件 , 极大地提高了Express项目的开发效率和体验
express.static
快速静态托管静态资源的内置中间件 , 例如 , HTML 文件 , 图片 ,css样式express.json
解析JSON格式的请求体数据express.urlencoded
解析URL-encoded格式的请求体数据
使用时只需要配置一下中间件即可
//配置全局中间件 //配置解析application/json格式数据的内置中间件 app.use(express.json()) //配置解析application/x-www-form-urlencoded格式数据的内置中间件 app.use(express.urlencoded({extended: false}))
看一个实例 , express.json
的使用 , 向服务器发送一个json格式的数据
const express = require('express') const app = express() //配置全局中间件 // 配置解析application/json格式数据的内置中间件 //通过这个中间件, 就会将解析出来的数据挂载到req.body身上 app.use(express.json()) app.post('/user', (req, res) => { //在服务器 , 可以使用req.body这个属性 //来接收客户端发送过来的请求体数据 //默认情况下 , 如不配置解析表单数据的中间件 , //则req.body默认等于undefined console.log(req.body) res.send('ok') }) app.listen(80, () => { console.log('app is running') })
express.urlencoded
的使用, 向服务器发送一个x-www-form-urlencoded
格式的数据
const express = require('express') const app = express() //配置全局中间件 //配置解析application/x-www-form-urlencoded格式数据的内置中间件 //需要传一个配置对象 //将解析出来的数据挂载到req.body身上 app.use(express.urlencoded({extended: false})) app.post('/book',(req,res) =>{ //在服务器端 , 可以通过req.body来获取json格式的表单数据和url-encoded格式的数据 //url-encoded是一种键值对形式的数据 console.log(req.body) res.send('OK') }) app.listen(80, () => { console.log('app is running') })
第三方的中间件
非 Express 官方内置的,而是由第三方开发出来的中间件,叫做第三方中间件。在项目中,大家可以按需下载并配置第三方中间件,从而提高项目的开发效率。
注意: express内置的express.urlencoded
中间件就是基于body-parser这个第三方中间件进一步封装出来的 , 4.16之后的版本就不需要安装上面提到的那个插件了
自定义中间件
需求描述与实现步骤
自己手动模拟一个类似于express.urlencoded
这样的中间件 , 来解析POST提交到服务器的表单数据
实现步骤
- 定义中间件
- 监听 req 的 data 事件
- 监听 req 的 end 事件
- 使用 querystring 模块解析请求体数据
- 将解析出来的数据对象挂载为 req.body
- 将自定义中间件封装为模块
定义中间件
使用app.use()
来配置全局中间件
app.use((req,res,next){ //..... })
监听req的data事件
在中间件中国 , 需要监听req对象的data事件, 来获取客户端发送到服务器的数据
如果数据量比较大 , 无法一次性发送完毕 , 则客户端会把数据切割后 , 分批发送到服务器
所以data事件可能会触发多次 , 每一次触发data事件 , 获取到数据只是完整数据的一部分, 需要手动对接收到的数据进行拼接
//待拼接的字符串 let str = '' req.on('data',(chunk) =>{ str += chunk; })
监听req的end事件
当请求体数据接收完毕之后, 会自动触发req的end 事件
因此我们可以在req的end事件中 , 拿到并处理完整的请求体数据
使用querystring模块解析请求体数据
Node.js内置了一个querystring
模块 , 专门用来处理查询字符串, 通过这个模块提供的parse()
函数 , 可以轻松把查询字符串, 解析成对象的格式
req.on('end',()=>{ //在str中存放的是完整的数 // console.log(str) //把字符串解析为对象格式 const body = qs.parse(str) console.log(body) })
整体代码
const express = require('express') const res = require('express/lib/response') const qs = require('querystring') const app = express() //这是解析表单数据的中间件 app.use((req,res,next)=>{ //定义中间件的逻辑处理 //待拼接的字符串 let str = '' req.on('data',(chunk) =>{ str += chunk; }) req.on('end',()=>{ //在str中存放的是完整的数 // console.log(str) //把字符串解析为对象格式 const body = qs.parse(str) console.log(body) req.body = body next() }) }) app.post('/user',(req,res)=>{ res.send(req.body) }) app.listen(80,()=>{ console.log('app is running') })
将解析出来的数据对象挂载为req.body
因为整个过程中, 中间件和路由共享同一份req
和res
, 所以可以在上游中间件中挂载自定义属性
red.body = body
将自定义中间件封装为模块
为了优化代码结构, 我们把自定义的中间件函数 , 封装为独立的模块, 向外共享这个函数
const qs = require('querystring') //定义一个中间件函数 my_urlencoded = (req,res,next)=>{ //定义中间件的逻辑处理 //待拼接的字符串 let str = '' req.on('data',(chunk) =>{ str += chunk; }) req.on('end',()=>{ //在str中存放的是完整的数 // console.log(str) //把字符串解析为对象格式 const body = qs.parse(str) console.log(body) req.body = body next() }) } module.exports = { my_urlencoded }
声明
此篇文章 根据黑马程序员视频教程和课件 整理而来
黑马程序员Node.js视频教程
https://www.bilibili.com/video/BV1a34y167AZ?spm_id_from=333.337.search-card.all.click
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现