路由 + Express 路由
Posted 鲸渔要加油
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了路由 + Express 路由相关的知识,希望对你有一定的参考价值。
学习目标
◆ 能够熟练 express 路由的使用
◆ 掌握中间件的使用方式
◆ 能够说出中间件分为哪几类
◆ 了解如何自定义中间件
◆ 能够基于 exprss 写接口
◆ 能够说出解决跨域问题的方法 cors
◆ 掌握如何实现 JSONP 接口
一、Express 路由
1. 路由的概念
- 路由就是 映射关系
- 根据不同的用户 URL 请求,返回不同的内容
- 本质:URL 请求地址与服务器资源之间的对应关系
2. Express 中的路由
1.在 Express
中,路由指的是客户端的请求与服务器处理函数之间的映射关系
2.Express
中的路由分 3 部分组成,分别是请求的类型、请求的 URL 地址、处理函数
app.METHOD(PATH, HANDLER)
3.Express
中的路由的例子
// 匹配 GET 请求,且请求 URL 为 /
app.get('/', function(req, res){
res.send('Hello World!')
})
// 匹配 POST 请求,且请求 URL 为 /
app.post('/', function(req, res){
res.sebd('Got a POST request')
})
3. 路由的匹配过程
1.每当一个请求到达服务器之后,需要先经过路由的匹配,只有匹配成功之后,才会调用对应的处理函数
2.在匹配时,会按照路由的顺序进行匹配,如果请求类型和**请求的 URL **同时匹配成功,则 Express
会将这次请求,转交给对应的 function 函数进行处理
3.路由匹配的注意点
按照定义的先后顺序进行匹配
请求类型和请求的URL同时匹配成功,才会调用对应的处理函数
4. Express 路由最简单的用法
1.在 Express
中使用路由最简单的方式,就是把路由挂载到 app
上
2.案例代码
const express = require('express')
// 创建 web 服务器,命名为 app
const app = express()
// 挂载路由
app.get('/', (req, res) => {
res.send('Hello, World')
})
app.post('/', (req, res) => {
res.send('Hello, Tom')
})
app.listen(3000, () => {
console.log('running……')
})
5. 模块化路由
1.为了方便对路由进行模块化的管理,Express
不建议将路由直接挂载到 app
上,而是推荐将路由抽离为单独的模块,将路由抽离为单独模块的步骤如下
- 创建路由模块对应的
.js
文件 - 调用
express.Router()
函数创建路由对象 - 向路由对象上挂载具体的路由
- 使用
module.exports
向外共享路由对象 - 使用
app.use()
函数注册路由模块
2.案例代码
// 1. 导入 express
const express = require('express')
// 2. 创建路由对象
const router = express.Router()
// 3. 挂载获取用户列表的路由
router.get('/user/list', (req, res) => {
res.send('用户列表')
})
// 4. 挂载添加用户列表的路由
router.post('/user/add', (req, res) => {
res.send('添加用户')
})
// 5. 向外导出路由对象
module.exports = router
6. 注册路由模块
- 导入路由模块
- 使用
app.use()
注册路由模块 - 案例代码
const express = require('express')
const app = express()
// 导入路由模块
const userRouter = require('./002-router')
// 使用 app.use() 注册路由模块
app.use(userRouter)
app.listen(3000, () => {
console.log('running……')
})
7. 为路由模块添加前缀
- 类似于托管静态资源写法,为静态资源统一挂载访问前缀一样
- 注意,添加了路由前缀后,访问的路由的时候,也应该加上前缀
- 案例代码
const express = require('express')
const app = express()
// 导入路由模块
const userRouter = require('./002-router')
// 使用 app.use() 注册路由模块
// 给路由模块添加统一得到访问前缀 /api
app.use('/api', userRouter)
app.listen(3000, () => {
console.log('running……')
})
二、中间件
1. 中间件的概念
所谓的中间件(Middleware
),特指业务流程的中间处理环节
2. Express 中间件的调用流程
当一个请求到达 Express 的服务器之后,可以连续调用多个中间件,从而对这次请求进行预处理
3. Express 中间件的格式
1.Express 的中间件,本质上就是一个 function
处理函数
2.注意:中间件函数的形参列表中,必须包含 next 参数。而路由处理函数中只包含 req
和 res
4. next 函数的作用
next 函数是实现多个中间件连续调用的关键,它表示把流转关系转交给下一个中间件或路由
5. 定义中间件函数
const express = require('express')
const app = express()
// 定义一个最简单的中间件函数
const kw = () => {
console.log('这是最简单的中间件函数')
// 把流转关系,转交给下一个中间件或者路由
next()
}
app.listen(80, () => {
console.log('running……')
})
6. 全局生效的中间件
1.客户端发起的任何请求,到达服务器之后,都会触发的中间件,叫做全局生效的中间件
2.通过调用 app.use(中间件函数)
,即可顶一个全局生效的中间件
const express = require('express')
const app = express()
// 定义一个最简单的中间件函数
const kw = (req, res, next) => {
console.log('这是最简单的中间件函数')
// 把流转关系,转交给下一个中间件或者路由
next()
}
// 全局生效的中间件
app.use(kw)
app.get('/', (req, res) => {
console.log('调用了 / 这个路由')
res.send('Home page')
})
app.get('/user', (req, res) => {
console.log('调用了 /user 这个路由')
res.send('User page')
})
app.listen(3000, () => {
console.log('running……')
})
7. 定义全局中间件的简化形式
定义全局中间件也可以将中间件函数直接挂载到 app.use()
上面
const express = require('express')
const app = express()
// 定义全局中间件的简化形式
app.use((req, res, next) => {
console.log('定义全局中间件的简化形式')
next()
})
app.get('/', (req, res) => {
console.log('调用了 / 这个路由')
res.send('Home page')
})
app.get('/user', (req, res) => {
console.log('调用了 /user 这个路由')
res.send('User page')
})
app.listen(3000, () => {
console.log('running……')
})
8. 中间件的作用
1.多个中间件之间,共享同一份 req
和 res
,基于这样的特性,我们可以在上游 的中间件中,统一为 req
或 res
对象添加自定义的属性和方法,供下游的中间件或路由进行使用
2.案例代码
const express = require('express')
const app = express()
// 定义全局中间件的简化形式
app.use((req, res, next) => {
// 获取到请求达到服务器的时间
const time = Date.now()
// 为 req 对象,挂载自定义属性,从而把时间共享给后面的所有路由
req.startTime = time
next()
})
app.get('/', (req, res) => {
res.send('Home page' + req.startTime)
})
app.get('/user', (req, res) => {
res.send('User page' + req.startTime)
})
app.listen(3000, () => {
console.log('running……')
})
9. 定义多个全局中间件
1.可以使用 app.use()
连续定义多个全局中间件,客户端请求到达服务器之后,会按照中间件定义的先后顺序依次执行
2.案例代码
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(3000, () => {
console.log('running……')
})
10. 局部生效的中间件
1.不使用 app.use()
定义的中间件,叫做局部生效的中间件
2.案例代码
const express = require('express')
const app = express()
// 定义中间件函数 mv1
const mv1 = (req, res, next) => {
console.log('这是中间件函数')
next()
}
// mv1 这个中间件只在 "当前路由中生效",这种用法属于 "局部生效的中间件"
app.get('/', mv1, (req, res) => {
res.send('Home Page')
})
app.get('/user', (req, res) => {
res.send('User Page')
})
app.listen(3000, () => {
console.log('running……')
})
11. 定义多个局部中间件
1.可以在路由中,通过如下两种等价的方式,使用多个局部中间件
2.案例代码
const express = require('express')
const app = express()
// 定义中间件函数 mv1
const mv1 = (req, res, next) => {
console.log('这是第一个中间件函数')
next()
}
// 定义中间件函数 mv2
const mv2 = (req, res, next) => {
console.log('这是第二个中间件函数')
next()
}
// mv1、mv2 这个中间件只在 "当前路由中生效",这种用法属于 "局部生效的中间件"
app.get('/', mv1, mv2, (req, res) => {
res.send('Home Page')
})
// 也可以使用数组的形式绑定两个中间件
app.get('/user', [mv1, mv2], (req, res) => {
res.send('Home Page')
})
app.listen(3000, () => {
console.log('running……')
})
12. 中间件的5个使用注意事项
- 一定要在路由之前注册中间件
- 客户端发送过来的请求,可以连续调用多个中间件进行处理
- 执行完中间件的业务代码之后,不要忘记调用
next()
函数 - 为了防止代码逻辑混乱,调用
next()
函数后不要再写额外的代码 - 连续调用多个中间件时,多个中间件之间,共享
req
和res
对象
三、中间件的分类
为了方便大家理解和记忆中间件的使用,Express 官方把常见的中间件用法,分成了 5 大类,分别是
- 应用级别的中间件
- 路由级别的中间件
- 错误级别的中间件
- Express 内置的中间件
- 第三方的中间件
四、自定义中间件
- 需求描述: 自己手动模拟一个类似于
express.urlencoded
这样的中间件,来解析POST
提交到服务器的表单数据 - 实现步骤:
- 定义中间件
- 监听
req
的data
事件 - 监听
req
的end
事件 - 使用
querystring
模块解析请求体数据 - 将解析出来的数据对象挂载为
req.body
- 将自定义中间件封装为模块
五、基于 Express 写接口
- 创建基本的服务器
// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()
// 调用 app.listen 方法,指定端口号并启动 web 服务器
app.listen(3000, () => {
console.log('running……')
})
- 创建 API 路由模块
// router.js
const express = require('express')
const router = express.Router()
// 在这里挂载对应的路由
module.exports = router
// index.js
// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()
// 导入路由模块
const router = require('./router.js')
// 把路由模块,注册到 app 上
app.use('/api', router)
// 调用 app.listen 方法,指定端口号并启动 web 服务器
app.listen(3000, () => {
console.log('running……')
})
- 编写 get 请求
const express = require('express')
const router = express.Router()
// 在这里挂载对应的路由
router.get('/book', (req, res) => {
// 通过 req.query 获取客户端通过查询字符串发送到服务器的数据
const query = req.query
// 调用 res.send() 方法,想客户端响应处理的结果
res.send({
status: 0, // 0 表示处理成功,1 表示处理失败
msg: 'GET 请求成功', // 状态描述
data: query // 需要响应给客户端的数据
})
})
module.exports = router
- 编写 post 请求
注意:如果要获取 URL-encoded 格式的请求体数据,必须配置中间件 `app.use(express.urlencoded({ extended: false }))
const express = require('express')
const router = express.Router()
// 在这里挂载对应的路由
// 定义 POST 接口
router.post('/book', (req, res) => {
// 通过 req.body 获取请求题中包含的 url-encoded 格式的数据
const body = req.body
// 调用 res.send() 方法,向客户端响应结果
res.send({
status: 0,
msg: 'Post 请求成功',
data: body
})
})
module.exports = router
// index.js
// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()
// 配置解析表单数据的中间件
//注意:如果要获取 URL-encoded 格式的请求体数据,必须配置中间件 app.use(express.urlencoded({ extended: false }))
app.use(express.urlencoded({ extended: false }))
// 导入路由模块
const router = require('./020-apiRouter')
// 把路由模块,注册到 app 上
app.use('/api', router)
// 调用 app.listen 方法,指定端口号并启动 web 服务器
app.listen(3000, () => {
console.log('running……')
})
六、cors
- 到目前为止,我们编写的
GET
和POST
接口,存在一个很严重的问题:不支持跨域请求 - 解决接口跨域问题的方案主要有两种
- CORS (主流的解决方案,推荐使用)
- JSONP (有缺陷的解决方案:只支持 GET 请求)
七、jsonp
- 概念:浏览器端通过
<script>
标签的src
属性,请求服务器上的数据,同时,服务器返回一个函数的调用。这种请求数据的方式叫做JSONP
- 特点
JSONP
不属于真正的Ajax
请求,因为它没有使用XMLHttpRequest
这个对象JSONP
仅支持GET
请求,不支持POST
、PUT
、DELETE
等请求
以上是关于路由 + Express 路由的主要内容,如果未能解决你的问题,请参考以下文章