初识Express
1
Express简介
- express是基于node.js平台,快速,开放,极简的web开发框架
- express的作用和node.js内置的http模块类似,专门创建web服务器的
- 本质:npm上的一个包,提供了快速创建web服务器的便捷方法
- 中文官网:http://www.expressjs.com.cn/
- express是基于http内置模块封装出来的
- 使用express,我们可以方便,快速的创建web网站的服务器或API接口的服务器
Express的基本使用
- 安装express
npm i express@4.17.1
- 创建基本的web服务器
- 将下面所出现的代码所有放入一个文件夹即可
//导入express
const express = require('express')
//创建web服务器
const server = express()
//调用server.lieten(端口号,启动完成后的回调函数),启动服务器
server.listen('80',()=>{
console.log('express server running at http://127.0.0.1')
})
get和post请求发起及给服务器相应
- 我们先运行vscode代码,启动,在发送请求
我们要测试这个的时候要使用postman工具进行测试get http://127.0.0.1/user send - server.get
- server.post
- res.send
//4.监听客户端的GET和POST请求,并向客户端响应具体的内容,这里这个监听行为相当于http里面的request请求,当时我们做的是直接将那个网址在浏览器运行,现在时使用postman发送这个请求,这边写的代码是监听他的请求。
//打印的在postman工具里面
server.get('/user',(req,res)=>{
//调用express提供的res.send()方法,向客户端提供一个JSON对象
res.send({name:'zx',age:20,gender:'男'})
})
server.post('/user',(req,res)=>{
res.send('请求成功')
})
获取url中携带的查询参数
我们先编写代码,然后重新在终端运行一下之后,使用postman工具进行测试get http://127.0.0.1/?name = sz&age = 20,这种方式直接和我们在下面写键值对是一样的
//这个/是地址,在后面拼接的是参数
server.get('/',(req,res)=>{
//通过req.query可以获取到客户端发送过来的查询参数
//默认情况下,req.query是一个空对象
console.log(req.query)//{}
res.send(req.query)
//我们可以使用req.query.name , req.query.age访问到查询参数
})
//返回值
{
"name":"sz",
"age":"20"
}
获取url中的动态参数
在写完代码之后,我们可以使用postman发送请求 get http://127.0.0.1/user/1 send
//注意:这里的:id是一个动态参数
server.get('/user/:id',(req,res)=>{
//req.params是动态匹配到的url参数,默认也是一个空对象
console.log(req.params)
res.send(req.params)
})
注意点
- 后面那个:id也可以是其他名字,后面返回的就是这个名字
- 后面也可以匹配好多个名字,:id/:name,后面我们返回的就是这两个属性组成的对象了
托管一个静态资源
- express.static():创建一个静态资源服务器,即如下,将clock目录下的各个文件都对外开放
const express = require('express')
const server = express()
//在这里,调用express.static()方法,快速的对外提供静态资源,这里写的就是路径
server.use(express.static('./clock'))
server.listen('8081',()=>{
console.log('express server running at http://127.0.0.1:8081')
})
- 注意下面访问的网址,是在我们的服务器之后直接拼接clock文件夹下面的路径,clock文件夹将不在出现,因为express在指定的静态目录中查找文件,并对外提供资源的访问路径
托管多个静态资源
- 如果需要托管多个静态资源,请多次调用express.static()函数
- 访问资源文件时,会先在第一个文件中进行查找,没有查找到才会去第二个文件中查找,以此类推
app.use(express.static('./clock'))
app.use(express.static('./files'))
挂载路径前缀
- 在前面的例子中,我们直接就不用写/clock,直接写index.html就可以访问,当我们想要在路径之中写上完整路径时我们需要在前面挂在前缀进行访问,如下:
// 写这个就相当于创建了一个web服务器,我们使用node运行服务器,将资源托管出去,可使用网址进行访问
const express = require('express')
const app = express()
app.use('/abc',express.static('./clock'))
app.use(express.static('./files'))
app.listen('8081',()=>{
console.log('express server running at http://127.0.0.1:8081')
})
- 本来第一个应该是clock(按照顺序),但是clock要求加上前缀,我们没有加,则出现的时files
- 加上之后是clock
nodemon工具的使用
- nodemon:在我们编写node.js的时候,我们的源文件代码会发生改变,每一次我们都需要crtl c结束之后,node 名称 重新启动服务器,这个工具可以帮我们自动监听代码的变化,并重启项目
安装 npm install -g nodemon
使用nodemon
- 我们只需要将原来的node 名称 改成nodemon 名称就可以
- 按下保存键,就会自动重新启动
const express = require('express')
const app = express()
app.use('/files',express.static('./clock'))
app.use(express.static('./files'))
app.listen('8081',()=>{
console.log('express server running at http://127.0.0.1:8081')
})
2
路由的概念
- 10086中的按键,不同的按键对应不同的服务,按键与服务之间的映射关系
Express中的路由
- 路由指的是客户端的请求和服务器处理函数之间的映射关系
- express中的路由分三部分组成,分别是请求的类型,请求的url地址,处理函数,格式如下:
app.method(path,handle)
路由的匹配过程
- 每一个请求到达服务器之后,需要先经过路由的匹配,匹配成功之后,才会调用相应的处理函数。
- 很多个路由,会按照路由的先后顺序进行匹配
- 请求类型和请求url必须同时匹配成功,才会调用对应的处理函数
路由的用法
最简单的用法
- 将一个文件夹被认为是一个项目,使用npm init -y 初始化创建package.json文件,然后我们使用npm install express@4.17.1安装会出现node_modules package-lock.json
const express = require('express')
const app = express()
app.get('/',(req,res)=>{
res.send('get request')
})
app.post('/',(req,res)=>{
res.send('post request')
})
app.listen('8081',()=>{
console.log('http://127.0.0.1:8081')
})
创建路由模块
- 一般我们不会像上面一样将路由直接挂载到app上,会让文件很大,单独创建一个路由模块
const express = require('express')
const app = express()
app.listen('8081',()=>{
console.log('http://127.0.0.1')
})
const express = require('express')
const router = express.Router()
router.get('/',(req,res)=>{
res.send('get user list')
})
router.post('/',(req,res) =>{
res.send('Add new user')
})
module.exports = router
注册路由组件
const express = require('express')
const app = express()
//1.导入路由模块
const router = require('./03.router')
//2.注册路由模块
app.use(router)
//app.use()函数的作用,就是来注册全局中间件 app.use(express.static('./files')
app.listen('8082',()=>{
console.log('http://127.0.0.1')
})
为路由模块添加统一前缀
//和托管静态资源一样
app.use('/yili',router)
中间件
可参考https://bbs.huaweicloud.com/blogs/368470
- 中间件就相当于污水处理厂的各个步骤,过程就是一一执行
中间件的格式
-
express的中间件,本质上就是一个function处理函数,格式如下
-
区别路由处理函数和中间件处理函数:就是看他的处理函数参数有没有包含next参数
-
中间件函数的形参列表中,必须包含 next 参数。而路由处理函数中只包含 req 和 res。
next函数的作用
- next函数是实现多个中间件连续调用的关键,它表示把流转关系交给下一个中间件或路由。
- 调用了next函数,表示该中间件已经处理完毕了,交给下一个中间件或者路由处理。
定义一个最简单的中间件函数
const express = require('express')
const app = express()
const nw = function(res,req,next){
console.log('这是最简单的中间件函数')
//当前业务处理完毕后,调用next函数,把流转关系,转交给下一个中间件或路由
next()
}
app.listen('8081',()=>{
console.log('http://127,0,0,1')
})
全局生效的中间件
- 客户端发起的任何请求,到达服务器之后,都会触发的中间件
- 通过调用app.use(中间件函数),即可定义一个全局生效的中间件函数
const nw = function(res,req,next){
console.log('这是最简单的中间件函数')
//当前业务处理完毕后,调用next函数,把流转关系,转交给下一个中间件或路由
next()
}
//全局生效的中间件
app.use(nw)
app.get('/',(req,res)=>{
res.send('/ page')
})
定义全局中间件的简化形式
- 上述代码可以修改为
app.use(function(res,req,next){
console.log('这是最简单的中间件函数')
//当前业务处理完毕后,调用next函数,把流转关系,转交给下一个中间件或路由
next()
})
app.get('/',(req,res)=>{
res.send('/ page')
})
中间件的作用
- 多个中间件之间,共享同一份 req 和 res。基于这样的特性,我们可以在上游的中间件中,统一为 req 或 res 对象添加自定义的属性或方法,供下游的中间件或路由进行使用。
- 例如现在我们定义一种需求:我们需要获取请求到达服务器的时间,如果我们不使用中间件的话,我们就需要在每一个路由中都进行单个的获取,这无疑非常的麻烦。
- 如果使用中间件将简便许多:
定义多个全局中间件
- 可以使用 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')
})
- 当我们对’/user’发送GET请求之后我们会得到如下的结果(服务器端):
局部生效的中间件
- 不使用 app.use() 定义的中间件,叫做局部生效的中间件,示例代码如下:
定义多个局部中间件
- 可以在路由中,通过如下两种等价的方式,使用多个局部中间件:
了解中间件的5个使用注意事项
- 一定要在路由之前注册中间件(有例外,错误级别的中间件)
- 客户端发送过来的请求,可以连续调用多个中间件进行处理
- 执行完中间件的业务代码之后,不要忘记调用 next() 函数
- 为了防止代码逻辑混乱,调用 next() 函数后不要再写额外的代码
- 连续调用多个中间件时,多个中间件之间,共享 req 和 res 对象
中间件的分类
应用级别的中间件
- 通过app.use(),app.get(),app.post(),绑定到app实例上的中间件,叫做应用级别的中间件,示例:
const nw = function(req,res,next){
next()
}
//应用级别的中间件(全局中间件)
app.use((req,res,next)=>{
next()
})
//应用级别的中间件(局部中间件)
app.get('/','nw',(req,res)=>{
res.send('/ page')
})
路由级别的中间件
- 绑定到express.Router()实例上的中间件,叫做路由级别的中间件,他的用法和应用级别的中间件没有任何区别,只不过,应用级别的中间件是绑定到app实例上,路由级别的中间件是绑定道router实例上
const router = express.Router()
//路由级别的中间件
router.use((req,res,next)=>{
next()
})
module.exports = router
const app = express()
app.use('/',router)
错误级别的中间件
作用:专门用来捕获整个项目中发生的异常错误,从而防止项目异常崩溃的问题。
格式:错误级别中间件的处理函数中,必须有四个形参,形参顺序从前到后,分别是(err,req,res,next)
app.get('/',(req,res)=>{ //1路由
throw new Error('服务器内部发生了错误!') //2 抛出了一个错误
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项目的开发效率和体验
- json格式的发送到服务器,想要拿到这个数据,就必须配置那文件的格式为json
1.express.static快速托管静态资源的内置中间件,例如:HTML文件,图片,css样式等(无兼容性)
2.express.json解析json格式的请求体数据(有兼容性,仅在4.16.0+版本中可用)
3.express.urlencoded解析URL-encoded格式的请求体数据(有兼容性,仅在4.16.0+版本中可用)
//配置解析application/json格式数据的内置中间件
app.use(express.json())
//配置解析application/x-www-form-urlencoded格式数据的内置中间件
app.use(express.urlencoded({extended:false}))
const express = require('express')
const app = express()
//除了错误级别的中间件,其他的中间件,必须在路由之前进行配置
//通过express.json这个中间件,解析表格中json数据格式
app.use(express.json())
app.post('/',(req,res)=>{
//在服务器下,可以使用req.body这个属性,来接受客户端发送过来的请求体数据
//默认情况下,如果不配置解析表单数据的中间件,则req.body默认等于undefined
console.log(req.body)
res.send('ok')
})
app.listen('8081',()=>{
console.log('http://127.0.0.1:8081')
})
const express = require('express')
const app = express()
//除了错误级别的中间件,其他的中间件,必须在路由之前进行配置
//通过express.json这个中间件,解析表格中json数据格式
app.use(express.urlencoded({extended:false}))
// app.use(express.json())
app.post('/',(req,res)=>{
//在服务器下,可以使用req.body这个属性,来接受客户端发送过来的请求体数据
//默认情况下,如果不配置解析表单数据的中间件,则req.body默认等于undefined
console.log(req.body)
res.send('ok')
})
app.listen('8081',()=>{
console.log('http://127.0.0.1:8081')
})
第三方的中间件
- 非express官方内置的,而是由第三方开发出来的中间件,叫做第三方中间件,在项目中,大家可以按需下载并配置第三方中间件,从而提高项目的开发效率。
例如:在express@4.16.0之前的版本中,经常使用body-parser这个第三方中间件,来解析请求体数据,步骤如下:
1.运行npm install body-parser 安装中间件
2.使用require导入中间件
3.调用 app.use()注册并使用中间件
const express = require('express')
const app = express()
const parser = require('body-parser')
app.use(parser.urlencoded({extend:false}))
app.post('/',(req,res)=>{
//在服务器下,可以使用req.body这个属性,来接受客户端发送过来的请求体数据
//默认情况下,如果不配置解析表单数据的中间件,则req.body默认等于undefined
console.log(req.body)
res.send('ok')
})
app.listen('8081',()=>{
console.log('http://127.0.0.1:8081')
})
- express内置的express.urlencoded中间件,就是基于body-parser这个第三方中间件进一步封装出来的
自定义中间件
需求与步骤
- 自己手动模拟一个类似于express.urlencoded这样的中间件,来解析post提交到服务器的表单数据
实现步骤:
1.定义中间件
2.监听req的data事件
3.监听req的end事件
4.使用querystring模块解析请求体数据
5.将解析出来的数据对象挂载为req.body
6.将自定义中间件封装为模块
const express = require('express')
const qs = require('querystring')
const app = express()
//解析表单数据的中间件
//1.定义中间件
app.use((req,res,next)=>{
//定义具体的业务逻辑
//2.监听req的data事件
//如果数据量比较大,无法一次性发送完毕,则客户端会把数据切割后,分批送到服务器,所以data事件
//可能会多次触发,每一次触发,获取到的数据只是完整数据的一部分,需要手动对接受到的数据进行拼接
let str = ''
req.on('data' ,(chunk)=>{
//拼接 隐式转换为字符串
str += chunk
})
//当请求体接收完毕之后,会自动触发req的end事件,因此我们可以在req的end事件中,拿到并处理完整的请求体数据
//3.监听req的end事件
req.on('end',()=>{
//str中存放的是完整的请求体数据
console.log(str) //name=xx&gender=%E7%94%B7
//把字符串格式的请求体数据,解析成对象格式
//node.js内置了一个querystring模块,专门用来处理查询字符串,通过这个模块提供的parse()函数,
// 可以轻松把查询字符串,解析成对象的格式
//4.使用querystring模块解析请求体数据
const body = qs.parse(str)
// 5.将解析出来的数据对象挂载为req.body
req.body = body
console.log(body)
next()
})
})
app.post('/',(req,res)=>{
res.send(req.body)
})
app.listen('8081',()=>{
console.log('http://127.0.0.1:8081')
})
将自定义中间件封装为模块
- 08
const express = require('express')
const app = express()
//导入自己封装的中间件模块
const bodyparser = require('./09.my -body-parser')
//将自己定义的中间件函数,注册为全局可用的中间件
app.use(bodyparser)
app.post('/',(req,res)=>{
res.send(req.body)
})
app.listen('8081',()=>{
console.log('http://127.0.0.1:8081')
})
- 09
const qs = require('querystring')
const bodyparser = (req,res,next)=>{
let str = ''
req.on('data' ,(chunk)=>{
//拼接 隐式转换为字符串
str += chunk
})
req.on('end',()=>{
console.log(str)
const body = qs.parse(str)
req.body = body
console.log(body)
next()
})
}
module.exports = bodyparser
使用express写接口
1.创建基本的服务器
2.创建api路由模块
3.编写GET接口
4.编写post接口
- 10.使用express写接口.js
const express = require('express')
const app = express()
//配置解析表单数据的中间件
app.use(express.urlencoded({extend:false}))
const router = require('./11、apirouter')
app.use('/api',router)
app.listen('8081',()=>{
console.log('http://127.0.0.1:8081')
})
- 11.apirouter.js
const express = require('express')
const router = express.Router()
router.get('/get',(req,res)=>{
//1.获取到客户端通过查询字符串,发送到服务器的数据
const query = req.query
//2.调用res.send方法,把数据响应给客户端
res.send({
status:0, //状态:0表示成功,1表示失败
msg:'get请求成功', //状态描述
data:query //需要响应给客户端的具体数据
})
})
router.post('/post',(req,res)=>{
//1.获取到客户端通过请求体,发送到服务器的url - encoded数据
const body = req.body
//2.调用res.send方法,把数据响应给客户端
res.send({
status:0, //状态:0表示成功,1表示失败
msg:'get请求成功', //状态描述
data:body //需要响应给客户端的具体数据
})
})
module.exports = router