这是我参与「第五届青训营 」伴学笔记创作活动的第 9 天
Express和中间件
Express(介绍和基本操作)
Express是一个第三方库,作用是可以替代http模块使用。
Express 下载 npm i express
基本操作
1.创建基本的web服务器
2.监听客户端的get请求(app.get())
3.监听客户端的post请求(aap.post())
4.把响应的内容放回给客户端(res.send())
5.获取url中携带的查询参数(res.query)
6.获取url中的动态参数(req.params)
Express托管静态资源
1. express.static()
Express提供了一个非常好用的函数, 叫做express.static(),通过它,我们可以非常方便地创建一个静态资源服务器,
例如,通过如下代码就可以将public下的图片、CSS 文件、JavaScript 文件对外开放访问了:
app.use(express.static('public'))
如果有多个访问静态资源文件时,express.static() 函数会根据目录的添加顺序查找所需的文件:
app.use(express.static('public'))
app.use(express.static('.file'))
现在,你就可以访问public中的所有文件了,例如:
http://localhost:3000/images/bg.jpg
http://localhost:3000/css/style.css
http://localhost:3000/js/login.js
注意: Express 在指定的静态目录中查找文件,并对外提供资源的访问路径。
因此,存放静态文件的目名不会出现在URL中。
举个例子:
我们的clock目录下为这样 ,这样我们直接访问http://127.0.0.1/index.html,则可以看到我们想要暴露出去的静态文件。
const express = require('express')
const app = express()
// 在这里,调用 express.static() 方法,快速的对外提供静态资源
app.use(express.static('./clock'))
app.listen(80, () => {
console.log('express server running at http://127.0.0.1')
})
如果希望在托管的静态资源访问路径之前,挂载路径前缀,则可以使用如下的方式:
app.use('/public', express.static('./public'))
现在,你就可以通过带有/public前缀地址来访问public绿中的文件了:
http://localhost:3000/public/images/kittenjpg
http://localhost:3000/public/css/style.css
http://localhost:3000/public/js/app.js .
Express路由
在Express中,路由指的是客户端的请求与服务器处理函数之间的映射关系。
Express中的路由分3部分组成,分别是请求的类型、请求的URL地址、处理函数。
基本用法:
const express = require('express')
const app = express()
// 挂载路由
app.get('/', (req, res) => {
res.send('hello world.')
})
app.post('/', (req, res) => {
res.send('Post Request.')
})
app.listen(80, () => {
console.log('http://127.0.0.1')
})
但是一般我们实际开发时会有很多路由,因此我们应该学习下模块化路由的用法,操作起来很简单。
// 这是路由模块
// 1. 导入 express
const express = require('express')
// 2. 创建路由对象
const router = express.Router()
// 3. 挂载具体的路由
router.get('/user/list', (req, res) => {
res.send('Get user list.')
})
router.post('/user/add', (req, res) => {
res.send('Add new user.')
})
// 4. 向外导出路由对象
module.exports = router
const express = require('express')
const app = express()
// 1. 导入路由模块
const router = require('./03.router')
// 2. 注册路由模块
//调用路由,在这里我们不仅可以直接调用路由,并且可以给路由中模块tian'jia前缀,
app.use('/api', router)
// 注意: app.use() 函数的作用,就是来注册全局中间件
app.listen(80, () => {
console.log('http://127.0.0.1')
})
中间件
在之前的express.static或者刚刚的路由中,我们都频繁的使用了app.use()这个api,这个api的作用,就是来注册全局中间件。
首先我们来介绍一下什么是中间件
中间件的概念
中间件(Middleware ),特指业务 流程的中间处理环节。
中间件的调用流程
当一个请求到达Express的服务器之后,可以连续调用多个中间件,从而对这次请求进行预处理。
而在我们express中,中间件本质就是一个处理东西的函数。
在中间件动数的形参列表中,必须含next参数。而路由处理函数只包含req和res。
因此我们在客户端发送一个请求给后端时,这个请求进入中间件会经历一个个中间件函数进行处理,每个中间件函数之间用next参数传递。最后传给路由,路由在响应给客户端。(注意路由没有next参数,所有路由必定是要放在最后一个函数)
用一个简单的例子来理解中间件函数
const express = require('express')
const app = express()
// 写法1 定义一个最简单的中间件函数
// const mw = function (req, res, next) {
// console.log('这是最简单的中间件函数')
// // 把流转关系,转交给下一个中间件或路由
// next()
// }
// // 将 mw 注册为全局生效的中间件
// app.use(mw)
// 写法2 这是定义全局中间件的简化形式
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(80, () => {
console.log('http://127.0.0.1')
})
中间件的作用
多个中间件之间,共享同一份req和res。基于这样的特性,我们可以在上游的中间件中,统一为req或res对象添
加自定义的属性或方法,供下游的中间件或路由进行使用。
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(80, () => {
console.log('http://127.0.0.1')
})
全局中间件和局部中间件
全局中间件:可以使用app.use(连续定义多个全局中间件。客户端请求到达服务器之后,会按照中间件定义的先后顺序依次进行
定义全局中间件案例:
const express = require('express')
const app = express()
// 定义第一个全局中间件
app.use((req, res, next) => {
console.log('调用了第1个全局中间件')
next()
})
// 定义第二个全局中间件
app.use((req, res, next) => {
console.log('调用了第2个全局中间件')
next()
})
// 定义一个路由
app.get('/user', (req, res) => {
res.send('User page.')
})
app.listen(80, () => {
console.log('http://127.0.0.1')
})
局部中间件: 不使用app.use()定义的中间件,叫做局部生效的中间件,
定义局部中间件案例:
// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()
// 1. 定义中间件函数
const mw1 = (req, res, next) => {
console.log('调用了第一个局部生效的中间件')
next()
}
const mw2 = (req, res, next) => {
console.log('调用了第二个局部生效的中间件')
next()
}
// 2. 创建路由
app.get('/', [mw1, mw2], (req, res) => {
res.send('Home page.')
})
app.get('/user', (req, res) => {
res.send('User page.')
})
// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(80, function () {
console.log('Express server running at http://127.0.0.1')
})
中间件的分类
1.应用型中间件:
通过app.use0或app.get(或app.post() ,绑定到app实例上的中间件,叫做应用级别的中间件,代码示例如下:
2.路由型中间件
绑定到express. Router()实例上的中间件,叫做路由级别的中间件。它的用法和应用级别中间件没有任何区别。只不过,应用级别中间件是绑定到app实例上,路由级别中间件绑定到router实例上。
3.错误型中间件
错误级别中间件的作用:专[ ]用来捕获整个项目中发生的异常错误,从而防止项目异常崩溃的问题。
格式:错误级别中间件的function处理函数中,必须有4个形参,形参顺序从前到后,分别是(err, req, res, next)。
案例:
// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()
// 1. 定义路由
app.get('/', (req, res) => {
// 1.1 人为的制造错误
throw new Error('服务器内部发生了错误!')
res.send('Home page.')
})
// 2. 定义错误级别的中间件,捕获整个项目的异常错误,从而防止程序的崩溃
app.use((err, req, res, next) => {
console.log('发生了错误!' + err.message)
res.send('Error:' + err.message)
})
// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(80, function () {
console.log('Express server running at http://127.0.0.1')
})
注意:错误型的中间件必须放在其它中间件(包括路由)后面,才能检测出错误。
4.Express内置中间件
自Express 4.16.0版本开始,Express 内置了3个常用的中间件,极大的提高了Express 项目的开发效率和体验:
express.static快速托管静态资源的内置中间件,例如: HTML 文件、图片、CSS 样式等(无兼容性)
expressjson解析JSON格式的请求体数据(有兼容性,仅在4.16.0+版本中可用)
express.urlencoded解析URL-encoded格式的请求体数据(有兼容性,仅在4.16.0+版本中可用)
// 注意:除了错误级别的中间件,其他的中间件,必须在路由之前进行配置
// 通过 express.json() 这个中间件,解析表单中的 JSON 格式的数据
app.use(express.json())
// 通过 express.urlencoded() 这个中间件,来解析 表单中的 url-encoded 格式的数据
app.use(express.urlencoded({ extended: false }))
其它
req中常用属性:
req.query获取url中携带的查询参数
req.params获取url中的动态参数
req.body获取请求体中包含的 url-encoded 格式的数据
res中常用的属性:
res.send调用 res.send() 方法,向客户端响应结果