1. 什么是express
express是基于Node.js平台,快速、开放、极简的Web开发框架
通俗的理解:express的作用和Node.js内置的http模块类似,是专门用来创建web服务器
的
1.1 http模块和express的关系
express是基于内置的http模块进一步封装出来的,能极大地提高开发效率
2. express能做什么
对于前端程序员,最常见的两种服务器,分别是:
web网站服务器
:专门向外提供web网页资源
的服务器api接口服务器
:专门向外提供api接口
的服务器
使用express,我们可以方便快捷地创建web网站的服务器或api接口的服务器
3. express的基本使用
3.1 安装
此处安装指定了版本
npm i express@4.17.1
3.2 创建最基本的web服务器
// 导入express
const express = require('express')
// 创建web服务器
const app = express()
// 启动服务器
app.listen(80,() => {
console.log('express server running at http://127.0.0.1');
})
3.3 监听get请求
// req请求对象,res响应对象
app.get('客户端的请求url',(req,res) => { /* 处理函数 */})
3.4 监听post请求
app.post('客户端的请求url',(req,res) => { /* 处理函数 */})
3.5 把内容响应给客户端
通过res.send()
方法,可以将处理好的内容,发送给客户端
app.get('/user',(req,res) => {
// 向客户端响应一个JSON对象
res.send({ name: 'AIpoem', age: 20})
})
app.post('/user',(req,res) => {
// 向客户端响应一个文本字符串
res.send('请求成功')
})
3.6 获取url中携带的查询参数
通过req.query
对象,可以访问到客户端通过查询字符串的形式,发送到服务器的参数:
app.get('/sendQuery',(req,res) => {
// req.query默认是一个空对象
// 客户端使用 ?name=AIpoem&age=20 这种查询字符串的形式,发送到服务器的参数
// 可以通过req.query对象访问到
res.send(req.query)
})
3.7 获取url中的动态参数
通过 req.params
对象,可以访问到url中,通过:
匹配到的动态参数:
// url中,可以通过 :参数名 的形式,匹配动态参数值
app.get('/user/:id',(req,res) => {
// req.params默认是一个空对象
res.send(req.params)
})
动态参数也不是只能有一个,可以设置多个
// url中,可以通过 :参数名 的形式,匹配动态参数值
app.get('/user/:id/:name',(req,res) => {
// req.params默认是一个空对象
res.send(req.params)
})
4. 托管静态资源
4.1 express.static()
通过express.static()
,我们可以非常方便地创建一个静态资源服务器
例如,通过以下代码就饿可以将public
目录下的图片、css文件、js文件对外开放访问
app.use(express.static('public'))
可以访问public目录中的所有文件了:
http://127.0.0.1/1.js
http://127.0.0.1/123.png
注意:存放静态资源文件的目录名不会出现在url中
4.2 托管多个静态资源目录
如果要托管多个静态资源目录,则多次调用express.static()
函数
app.use(express.static('public1'))
app.use(express.static('public2'))
4.3 挂在路径前缀
如果希望在托管的静态资源访问路径
之前,挂载路径前缀,则可以使用以下方式:
app.use('/public',express.static('public'))
可以通过带有/public
前缀的地址来访问public目录中的所有文件了:
http://127.0.0.1/public/1.js
http://127.0.0.1/public/123.png
5. nodemon
编写调试Node.js项目时,如果修改了项目的代码,需要频繁地手动close掉然后再重新启动,使用nodemon这个工具,当代码被修改后,nodemon会自动重启项目
5.1 安装nodemon
npm i nodemon -g
5.2 使用nodemon
node app.js
# 上面的终端命令替换成下面的终端命令
nodemon app.js
6. express路由
在express中,路由指的是客户端的请求
和服务器处理函数
之间的映射关系
express中的路由由三部分组成,分别是请求类型
、请求url
、处理函数
例如:
// req请求对象,res响应对象
app.get('客户端的请求url',(req,res) => { /* 处理函数 */})
当一个请求到达服务器后,需要先经过路由的匹配,匹配成功后才会调用对应的处理函数(如果请求类型和请求url同时匹配成功,则express会将这次请求转交给对应的函数处理
6.1 模块化路由
为了方便对路由进行模块化
管理,express不建议将路由直接挂载到app上(const app = express()
),而是推荐将路由抽离为单独的模块
将路由抽离为单独模块的步骤:
- 创建路由模块对应的
.js
文件 - 调用
express.Router()
函数创建路由对象 - 向路由对象上挂载具体的路由
- 使用
module.exports
向外导出路由对象
// user.js
const express = require('express')
const router = express.Router() // 创建路由对象
router.get('/user/list',(req,res) => { // 向路由对象上挂载获取用户列表的路由
res.send('get your list')
})
router.post('/user/add',(req,res) => { // 向路由对象上挂载添加用户的路由
res.send('add new user')
})
module.exports = router // 向外导出路由对象
- 使用
app.use()
函数注册路由模块
// main.js
const userRouter = require('./user.js') // 导入路由模块
app.use(userRouter) // 注册路由模块
⚠️:app.use()
函数的作用,就是来注册全局中间件
6.2 为路由模块添加前缀
路由模块添加前缀的方式和为静态资源挂载访问前缀是一样的
const userRouter = require('./user.js') // 导入路由模块
app.use('/api',userRouter) // 注册路由模块
加了/api
前缀之后,发送请求的url就变成了http://127.0.0.1/api/user/list
7. express中间件
中间件,特指业务流程的中间处理环节
7.1 express中间件的调用流程
当一个请求到达express的服务器之后,可以连续调用多个中间件,从而对这次请求进行预处理
7.2 express中间件的格式
express的中间件本质上就是一个处理函数,格式如下:
⚠️:中间件函数的形参列表中必须包含next参数
,而路由处理函数中只包含req
和res
7.2.1 next函数的作用
只要在某一个中间件中调用了next()
就表示处理完毕了,要将处理结果转交给下一个中间件或路由
7.3 使用中间件
7.3.1 定义中间件函数
const mw = (req,res,next) => {
console.log('一个简单的中间件函数');
// 当前中间件的业务处理完毕后,必须调用next()函数
next();
}
7.3.2 全局中间件
客户端发起的任何请求
,到达服务器之后,都会触发
的中间件,就是全局中间件
通过调用app.use(中间件函数)
,可以定义一个全局生效的中间件
const mw = (req,res,next) => {
console.log('一个全局生效的中间件函数')
// 当前中间件的业务处理完毕后,必须调用next()函数
next()
}
// 将mw注册为全局生效的中间件
app.use(mw)
定义全局中间件的简化形式:
app.use((req,res,next) => {
console.log('一个全局生效的中间件函数')
next()
})
定义多个全局中间件:
还是使用app.use()
连续定义多个即可,客户端请求到达服务器之后,会按照中间件定义的先后顺序依次进行调用
7.3.3 局部中间件
不使用app.use()
定义的中间件
如:
const mw1 = (req,res,next) => {
console.log('一个局部生效的中间件函数')
next()
}
// mw1中间件只在当前路由中生效
app.get('/home',mw1,(req,res) => {
res.send('home page')
})
// mw1中间件不会影响此路由
app.get('/user',(req,res) => {
res.send('user page')
})
定义多个局部中间件:
// 以下两种方式等价,顺序从左到右
app.get('/home',mw1,mw2,mw3,(req,res) => {res.send('home page')})
app.get('/home',[mw1,mw2,mw3],(req,res) => {res.send('home page')})
7.3.4 中间件分类
7.3.4.1 应用级别的中间件
通过app.use()
(全局)或app.get()
(局部)或app.post()
(局部)绑定到app实例上的中间件
7.3.4.2 路由级别的中间件
绑定到express.Router()
实例上的中间件
7.3.4.3 错误级别的中间件
专门用来捕获整个项目中发生的一场错误,从而防止项目异常崩溃的问题
错误级别中间件的处理函数中,必须有4个形参,分别是(err,req,res,next)
app.get('/',(req,res) => {
// 人为的制造错误
throw new Error('服务器内部发生了错误!')
// 这行代码执行不了
res.send('home page')
})
// throw error之后立即跳进错误级别中间件中,向客户端响应错误消息
app.use((err,req,res,next) => {
console.log('发生了错误' + err.message)
res.send('Error:' + err.message)
})
⚠️:错误级别的中间件必须注册在所有路由之后
7.3.4.4 express内置的中间件
express内置了3个常用的中间件
1.express.static
快速托管静态资源的内置中间件
2.express.json
解析JSON格式的请求体数据
// 配置解析 application/json 格式数据的内置中间件
app.use(express.json())
3.express.urlencoded
解析URL-encoded格式的请求体数据
// 配置解析 application/x-www-form-urlencoded 格式数据的内置中间件
app.use(express.urlencoded({ extended: false }))
例:
app.post('/user',(req,res) => {
// req.body接收客户端发送的请求体数据
// 如果不配置解析表单数据的中间件,req.body默认等于undefined
console.log(req.body) // undefined
})
在路由前配置好解析JSON格式请求体数据的中间件:
app.use(express.json())
app.post('/user',(req,res) => {
console.log(req.body) // { name: 'AIpoem', age: 20 }
})
7.3.4.5 第三方的中间件
这个没什么要记的(๑•̀ㅂ•́)و✧
中间件真的有、、折磨 ╮(╯▽╰)╭