Node.js初探(四)
1、初识express:
与http模块类似,对http进一步封装,专门用来创建web服务器
服务器:web网站服务器、API接口服务器
2、使用express创建最基本的服务器
(1)安装:npm i express@4.17.1
(2)创建简单的服务器以及请求
可以通过get和post进行请求处理
const express = require('express')
const app = express()
// 监听get请求
app.get('请求URL', function(req, res) {/* 处理函数 */})
// 监听post请求
app.post('请求URL', function(req, res) {/* 处理函数 */})
app.listen(3000, () => {
console.log('express server running at http://127.0.0.1');
})
(3)send()函数
-
可以在处理函数中调用
res.send(响应内容)
对当前请求进行响应 -
send()的响应内容可以是多种形式的
(4)获取URL中携带的查询参数:
-
req.query
:默认情况下,内容为{ } 请求URL:'/user' 完整URL:http://127.0.0.1:3000/user?uname=zs&age=18 req.query: { "name": "zs", "age": "18" }
(5)URL中的动态参数(可有多个动态参数):
-
req.params
:默认状态下,内容为{ } 请求URL:'/user/:uname/:age/:fav' 完整URL:http://127.0.0.1:3000/user/zs/18/lily req.params: { "uname": "zs", "age": "18", "fav": "lily" }
3、使用express托管静态资源
app.use(express.static(要托管的资源的路径)) app.use(express.static('./shopping'))
-
托管多个静态资源目录:多次调用app.use(express.static(资源路径))
-
访问静态资源时,express.static()会根据目录的添加顺序查找所需的文件
4、挂载路径前缀(在访问静态资源时,必须加上路径的前缀才能访问得到)
app.use(路径前缀, express.static(要托管的资源的路径)) app.use('/public', express.static('./shopping'))
5、路由:映射关系
(1)express中的路由
- 路由:客户端的请求与服务器处理函数之间的映射关系(包括请求方式、请求URL、处理函数)
(2)路由的匹配过程
按照定义的先后顺序进行匹配
请求类型和请求URL地址需要同时匹配成功,才会调用对应的处理函数
6、挂载路由
// app express对象
app.get('/', function(req, res) { res.send('111') })
app.post('/', function(req, res) { res.send('222') })
一般路由不建议直接挂载
7、模块化路由
/** router.js */
// 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 user list')
})
// 4、向外导出路由对象
module.exports = router
/** 使用test.js */
const express = require('express')
const app = express()
// 1、导入路由模块
const router = require('./03.router')
// 2、注册路由模块
app.use(router)
app.listen(80, () => {
console.log('http://127.0.0.1');
})
app.use()
:专门用来注册中间件的
app.use(访问前缀, router)
:也可以为路由模块挂载访问前缀,在访问路由时都需要加上
8、中间件:用户请求到路由匹配中间的过程(对请求进行预处理)
(1)express中间件的格式
本质是一个函数,在函数的形参列表中,必须包含next参数,next必须是最后一个参数
(2)next函数的作用
实现多个中间件连续调用,表示把流转关系转交给下一个中间件或路由
(3)定义中间件函数
const mw = function(req, res, next) {
console.log('这是一个中间件函数')
// 将流转关系,转交给下一个中间件函数
next()
}
(4)注册中间件
全局生效的中间件:app.use(中间件函数)
// 将mw注册为全局生效的中间件,中间件都要在路由的前面进行定义,发送请求先进入中间件
app.use(mw)
中间件的作用:
多个中间件之间,共享一根req和res,基于这样的特性,可以在上游的中间件中,统一为req或者res对象添加自定义的属性或方法,供下游的中间件或路由进行使用
app.use((req, res, next) => {
req.startTime = Date.now()
next()
})
app.get('/', (req, res) => {
res.send('Home' + req.startTime)
})
app.get('/user', (req, res) => {
res.send('User' + req.startTime)
})
定义多个全局中间件:
可以使用app.use()连续定义多个全局中间件,客户端请求到达服务器之后,会按照中间定义的先后顺序依次进行调用
app.use((req, res, next) => {
console.log('第一个')
next()
})
app.use((req, res, next) => {
console.log('第二个')
next()
})
局部生效的中间件:不使用app.use()定义的中间件
// 只会在/路由中才会打印'局部中间件',因为只给/路由注册
const mw = function(req, res, next) {
console.log('局部中间件')
next()
}
app.get('/', mw, (req, res) => {
res.send('Home')
})
app.get('/user', (req, res) => {
res.send('User')
})
定义多个局部中间件:
// 下面两个是等价的
app.get('/', mw1, mw2, (req, res) => { res.send('Page') })
app.get('/', (mw1, mw2), (req, res) => { res.send('Page') })
9、中间件的注意事项
(1)一定要在路由之前注册中间件
(2)客户端发过来的请求,可以连续调用多跟中间件进行处理
(3)执行完中间件的业务代码之后,不要忘记调用next()函数
(4)为了防止代码逻辑混乱,调用next()函数之后不要再写额外的代码
(5)连续调用多个中间件时,多个中间件之间,共享req和res对象
10、中间件的分类
(1)应用级别:app.use()/app.get()/app.post(),直接绑定到app实例上的中间件
(2)路由级别:绑定到express.Router()实例上的中间件(和应用级别用法相同)
(3)错误级别:
捕获整个项目中发生的异常错误,防止项目异常崩溃的问题
错误级别的中间件一定要写在路由之后
格式:
处理函数中,必须有4个形参,顺序从前到后,分别是(err, req, res, next)
const express = require('express')
const app = express()
// 1、定义路由
app.get('/', (req, res) => {
throw new Error('服务器内部发生了错误!')
res.send('Home page')
})
// 2、定义错误级别的中间件
app.use((err, req, res, next) => { // 错误级别中间件
console.log('Error:' + err.message); // 1、在服务端打印错误信息
res.send('Error:' + err.message) // 2、向客户端返回错误信息
})
app.listen(80, () => {
console.log('http://127.0.0.1');
})
11、Express内置的中间件
(1)express.static:静态托管资源
(2)express.json:解析JSON格式的请求体数据
(3)express.urlencoded:解析URL-encoded格式的请求体数据
// 解析JSON
app.use(express.json())
// 解析urlencoded
app.use(express.urlencoded({ extended: false }))
12、第三方中间件body-parser(解析请求体数据)
(1)安装:npm i body-parser
(2)使用require导入文件
(3)调用app.use()注册并使用中间件
const express = require('express')
const app = express()
const parser = require('body-parser')
app.use(parser.urlencoded({ extended: false }))
app.post('/', (req, res) => {
console.log(req.body);
res.send('ok')
})
app.listen(80, () => {
console.log('http://127.0.0.1');
})
内置请求体解析中间件就是对body-parser中间件的封装
13、自定义中间件:模拟express.urlencoded类似功能
(1)定义中间件
(2)监听req的data操作:查看是否有数据被提交
(3)监听req的end事件:数据已经发送完毕,服务端已经接收到了数据
(4)使用querystring模块解析请求体数据:
(5)将解析出来的数据对象挂载为req.body
(6)将自定义的中间件封装为模块
完整文件
const express = require('express')
const app = express()
const bodyParse = require('./bodyParse')
app.use(bodyParse)
app.post('/user', (req, res) => {
res.send(req.body)
})
app.listen(80, () => {
console.log('http://127.0.0.1');
})
可以将功能单独封装(// 步骤(6)),然后再注册路由中间件(bodyParse.js)
const qs = require('querystring')
// 步骤(1)
const bodyPaser = (req, res, next) => {
var str = ''
// 步骤(2)
req.on('data', chunk => {
str += chunk
})
// 步骤(3)
req.on('end', () => {
// 步骤(4)(5)
req.body = qs.parse(str)
next()
})
}
module.exports = bodyPaser