浅谈Node之express框架
浅谈Node之express框架
前言
今天终于到了重点话题了,开始学习express框架,在学习之前我们需要了解express的作用和背景。express是一个web框架,功能和http模块类似,只不过功能更加强大,开发效率更高,就像后端开发中从spring框架开发到了springboot框架的升级一样。
目录
- express基本使用
- express.static静态资源托管
- express.use注册中间件&挂载路径前缀
- express.Router()路由
- express中间件
1. express基本使用
express框架基本使用还是比较简单的,基本使用步骤可以分为如下4步:
1. 导入express框架
2. 创建服务器实例对象
3. 绑定事件
4. 监听端口,启动服务器
如果大家看了前面三个Node的文档,看到这里应该会有点印象,这个步骤实际上和我们使用http模块是一样的过程,下面用代码来具体看一下express的使用:
新建一个test.js文件,文件内容如下:
// 1. 导入express框架
const express = require('express')
// 2. 创建服务器实例对象
const app = express()
// 3. 绑定事件
app.get('/',function(req,res){
console.log("hh,你这次请求在访问我哦。。。")
})
// 4. 监听端口,绑定事件
app.listen(9999,function(){
console.log("服务器启动了!!!")
})
使用node命令运行该js文件
node test.js
然后在浏览器中输入localhost:9999,就可以访问到我们刚刚创建的web服务了。相信看到这里的小伙伴已经学会了express的基本使用了,是不是很简单了!既然express框架的使用和http模块代码量差不多,那么我们为什么就要选择使用express框架咧?那么肯定是express框架为我们提供了更加丰富的API操作,比如中间件、路由等高级功能,后面我也会学习后继续更新,大家可以耐心等待~
2. express静态托管
咋一看上去对”静态托管“这个名词很陌生高级,实际上就是创建一个静态资源服务器,比如文本、图片等各种资源的服务器。而要实现这种功能也很简单,只需要调用express的static方法即可。具体代码使用可以看如下案例:
// 1. 导入express框架
const express = require('express')
// 2. 创建服务器实例
const app = express()
// 3. 绑定事件(app.use实际上就是注册插件,可以理解成注册绑定静态资源路径)
app.use(express.static('./txt'))
// 4. 监听端口,开启服务器
app.listen(8888,function(){
console.log("服务器启动了")
})
我们在第3步中绑定事件,添加了静态资源,因此可以直接在浏览器中指定静态文件路径,从而获取静态资源
当然你会发现浏览器中输入的路径缺省了我们刚刚指定的/txt这一级路径,这是因为在使用app.use注册的时候默认是把静态资源的目录省略掉,从而便于直接访问路径内的静态资源。如果我们想要保持资源路径和访问路径一致,那我们可以在注册静态资源的时候指定访问前缀,如下代码形式
// 1. 导入express框架
const express = require('express')
// 2. 创建服务器实例
const app = express()
// 3. 绑定事件(app.use实际上就是注册插件,可以理解成注册绑定静态资源路径),指定访问静态资源路径前缀
app.use('/txt',express.static('./txt'))
// 4. 监听端口,开启服务器
app.listen(8888,function(){
console.log("服务器启动了")
})
3. express.use注册中间件&挂载路径前缀
中间件这似乎又是一个新名词,那么到底什么是中间件呢?其实就是中间处理环节,就好像土豆制造薯条中,需要经理很多中间环节,比如清洗、烘焙、切片、油炸等等。这些中间环节就像是我们的中间件。中间件的本质就是一个function处理函数,只不过这个处理函数会比较特殊,形参中会多出一个next形参,具体格式为:
中间件格式
function(req,res,next){
//内容
}
这些中间件的存在使我们不用关注过程,直接从输入通过中间件得到想要的输出,同时中间件也能提高模块的复用性,比如我们想要炸藕片,由于中间环节一致,我们可以直接使用这些中间件,过程就变成如下:
我们可以看一下中间件的调用流程:当一个请求到达Express的服务器后,可以连续调用多个中间件,从而对这次请求做预处理。
我们具体看一个中间件处理函数的使用:比如使用中间件获取访问路径,并将访问路径转换成大写形式:
const express = require('express')
const app = express()
// 注册中间件函数
app.use(function(req,res,next){
req.URL = req.url.toUpperCase()
next() // 调用next是继续执行后面的函数,包括中间件函数以及路由函数
})
app.get('/:path',(req,res) => {
console.log(req.URL);
console.log("hhh,这次请求你又调用我了");
})
app.listen(3000,()=>{
console.log("服务器启动了");
})
我们在终端中就可以看到中间件处理后的/APPOO结果了!!!这就是中间件函数的作用,可以帮助我们预处理操作,并将结果绑定在req对象中,在整个请求访问的链路中,各个中间件函数和路由函数(比如app.get())是共享一个req对象的,也就是说中间件中给req对象新增属性后,在后面的链路函数中都是可以访问到前面定义的属性的!!!
4. express.Router()路由
什么是路由呢?广义上讲路由就是一种映射关系 ,比如我们在拨通10086时,经常会提示你话费查询请按1,宽带业务请按2,人工服务请按0... 这种业务和数字的映射实际上就是一种路由。
那么在express中的路由又是什么呢?它是客户端的请求和服务器处理函数之间的映射关系,express中的路由包含3个部分,分别是请求的类型、请求的URL地址、处理函数,比如app.get('/',function(req,res){})
其中客户端的包括请求方式和指定请求URL地址,服务端处理函数会在接收到请求后处理。路由执行会按照定义的路由规则,从上往下做路由匹配执行,注意URL地址和请求类型都匹配成功后才会执行对应的处理函数。
express中最简单的路由规则定义是通过将路由挂载到服务器实例app上,但是这样会使代码过于冗长,不便于代码管理和维护,因此在实际开发中一般推荐将express路由抽离为单独的模块。具体express的模块化路由包含如下步骤:
//1、创建路由模块 module.js
//1.1 导入express
const express = require('express')
//1.2 创建路由对象
const router = express.Router()
//1.3 绑定事件
router.get('/',(req,res)=>{
console.log("hhh,这次请求访问的是我的路由模块定义的处理函数")
})
//1.4 导出模块对象
module.exports = router
//2. 创建运行模块
//2.1 导入express和路由对象
const express = require('express')
const router = require('./L0005_routerModule')
//2.2 创建服务器实例对象
const app = express()
//2.3 注册路由事件
app.use(router)
//2.4 监听端口,启动服务器实例
app.listen(3000,function(){
console.log("服务器已经启动了...")
})
然后运行node命令即可在客户端浏览器中访问了。
5. express中间件
中间件,特指业务流程中的中间处理环节,实际上就是一个或者多个预处理流程。
express的中间件和污水处理的流程实际上大相径庭,是对请求真正响应前的中间过程预处理,我们可以看一下express中间件的处理流程:
那么到底express中间件是什么呢?它本质上就是一个funciton处理函数,只不过这个处理函数比较特殊,它的定义形式为function(req,res,next)
,形参中多了一个next参数,从而实现了中间处理环节。那么很多小伙伴可能会有疑问,中间件和我们的路由有什么区别呢?其实路由也可以看作是一个中间件,只不过它到此为止即结束继续链式调用中间件的功能。
我们为什么一定要使用中间件呢?因为中间件间可以共享req和resp对象,这极大方便了我们在需要统一处理路由规则时需要有相同的预处理操作。比如需要记录每次不同的请求到达时间,如果不使用中间件,那我们就需要在每个路由规则的处理函数添加这么一句 const time = new Date()
来记录请求时间,但这是非常麻烦的。这时候我们只需要定义一个中间件,然后向服务器实例中注册该中间件,就能实现这个功能。定义的中间件函数如下
const mw = function(req,res,next){
const time = new Date();
req.time = time ; //req对象中绑定time属性,由于中间件和路由中的req共享,因此路由中的req可以访问到
next(); // 注意不要忘记写next()方法去调用后面的中间件
}
当一个请求到Express服务器后,可以连续调用一个或多个中间件,对这次请求预处理操作,最后通过路由响应给客户端。
5.2 中间件分类
常见的中间件可以分为如下五类:
- 应用级别中间件:中间件绑定在app服务器实例上,比如app.use()/app.get()/app.post()
- 路由级别中间件:中间件绑定在路由实例上,比如router.use()/router.get()/router.post()
- 错误级别中间件:用来捕获整个项目的异常错误,从而防止项目异常崩溃,错误级别中间件的funciton错误函数比较特殊,有4个参数
function(err,req,resp,next){}
,还有一个注意点是错误级别中间件定义在所有路由之后,这样在异常错误发生时才能被错误中间件捕获到。 - express内置中间件:express内置了一些中间件,比如
express.static()/express.json()/express.urlencoded({extended: false})
,这三个常用的内置中间件具体的作用分别如下:
express.static():快速托管静态资源的中间件
express.json():解析json格式的请求体数据
express.urlencoded({extended: false}):解析urlencoded格式的数据
这些内置中间件具体的使用语法都是一样的,通过
app.use(express.static(path))
app.use(express.json())
app.use(express.urlencoded({extended: false})) - 第三方中间件:非express官方内置的,而是由第三方开发出来的中间件,例如body-parser中间件,也是用来解析请求体数据的,具体使用方式和我们使用第三方模块方式是一样的,包括npm命令下载第三方中间件、require导入中间件、调用app.use()注册中间件
为了便于加深我们对中间件的理解,我们可以模仿实现一个类似express.urlencode的内置中间件,具体的过程如下:
- 定义中间件
- 监听req的data事件:监听req对象的data事件,来获取客户端发送到服务器的数据,如果数据量非常大时,客户端会把数据切分,分批发送到服务器端分批发送,每次都会触发data事件
- 监听req的end事件:当数据接收完毕,会自动触发end事件,因此我们可以在end事件内拿到并处理完整的请求体数据
- 使用querystring模块解析请求体数据:调用该模块,把字符串转换为对象
- 将解析出来的数据对象挂在为req.body:将解析出来的数据,挂载为req的自定义属性,命名为req.body供下游使用
const express = require('express')
const queryString = require('querystring')
const app = express()
app.use(function(req,res,next){
let str = ''
req.on('data', function(chunk){
str += chunk
})
req.on('end', function(){
console.log("请求的数据为:",str);
})
const body = queryString.parse(str)
req.body = body
next()
})
app.listen(3000,function(){
console.log("服务器启动了");
})
然后node命令启动服务器,通过postman工具测试post请求即可实现