文章目录
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