路由 + 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. 注册路由模块

  1. 导入路由模块
  2. 使用 app.use() 注册路由模块
  3. 案例代码
const express = require('express')
const app = express()

// 导入路由模块
const userRouter = require('./002-router')

// 使用 app.use() 注册路由模块
app.use(userRouter)

app.listen(3000, () => {
  console.log('running……')
})

7. 为路由模块添加前缀

  1. 类似于托管静态资源写法,为静态资源统一挂载访问前缀一样
  2. 注意,添加了路由前缀后,访问的路由的时候,也应该加上前缀
  3. 案例代码
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 参数。而路由处理函数中只包含 reqres
在这里插入图片描述


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.多个中间件之间,共享同一份 reqres,基于这样的特性,我们可以在上游 的中间件中,统一为 reqres 对象添加自定义的属性和方法,供下游的中间件或路由进行使用
在这里插入图片描述
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个使用注意事项

  1. 一定要在路由之前注册中间件
  2. 客户端发送过来的请求,可以连续调用多个中间件进行处理
  3. 执行完中间件的业务代码之后,不要忘记调用 next() 函数
  4. 为了防止代码逻辑混乱,调用 next() 函数后不要再写额外的代码
  5. 连续调用多个中间件时,多个中间件之间,共享 reqres 对象


三、中间件的分类

为了方便大家理解和记忆中间件的使用,Express 官方把常见的中间件用法,分成了 5 大类,分别是

  • 应用级别的中间件
  • 路由级别的中间件
  • 错误级别的中间件
  • Express 内置的中间件
  • 第三方的中间件


四、自定义中间件

  1. 需求描述: 自己手动模拟一个类似于 express.urlencoded 这样的中间件,来解析 POST 提交到服务器的表单数据
  2. 实现步骤:
    • 定义中间件
    • 监听 reqdata 事件
    • 监听 reqend 事件
    • 使用 querystring 模块解析请求体数据
    • 将解析出来的数据对象挂载为 req.body
    • 将自定义中间件封装为模块


五、基于 Express 写接口

  1. 创建基本的服务器
// 导入 express 模块
const express = require('express')

// 创建 express 的服务器实例
const app = express()

// 调用 app.listen 方法,指定端口号并启动 web 服务器
app.listen(3000, () => {
  console.log('running……')
})
  1. 创建 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……')
})
  1. 编写 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
  1. 编写 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

  1. 到目前为止,我们编写的 GETPOST 接口,存在一个很严重的问题:不支持跨域请求
  2. 解决接口跨域问题的方案主要有两种
    • CORS (主流的解决方案,推荐使用)
    • JSONP (有缺陷的解决方案:只支持 GET 请求)


七、jsonp

  1. 概念:浏览器端通过 <script> 标签的 src 属性,请求服务器上的数据,同时,服务器返回一个函数的调用。这种请求数据的方式叫做 JSONP
  2. 特点
    • JSONP 不属于真正的 Ajax 请求,因为它没有使用 XMLHttpRequest 这个对象
    • JSONP 仅支持 GET 请求,不支持 POSTPUTDELETE 等请求

以上是关于路由 + Express 路由的主要内容,如果未能解决你的问题,请参考以下文章

VSCode自定义代码片段11——vue路由的配置

VSCode自定义代码片段11——vue路由的配置

express路由及配置

express路由及配置

使 Angular 路由与 Express 路由一起工作

浅析Express中的路由与应用模式