Express 路由
概念
路由是什么
组成
- 请求类型
- URL
- 处理函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| app.method(PATH, handler)
app.get('/', function(req, res){ res.send('Hello World') })
app.post('/', function(req, res){ res.send('Hello World') })
|
请求到达服务器之后,需要先进性路由匹配,匹配成功之后才会调用相应的处理函数
匹配成功的条件:
- 请求类型匹配(GET,POST,HEAD…)
- 请求URL匹配(URL相同)

简单使用
初始化node项目的 package.json
安装express
1
| npm install express@4.17.1
|
简单使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const express = require('express')
const app = express()
app.get('/', (req, res) => { res.send('Hello World') })
app.post('/', (req, res) => { res.send('post Hello World') })
app.listen(8080, ()=> { console.log('listening on 8080') })
|
获取查询参数
静态参数
使用req.query获取查询参数,默认是json格式
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const express = require('express')
const app = express()
app.get('/info', (req, res) => { res.send(req.query) console.log(req.query) })
app.listen(8080, () =>{ console.log('listening on 8080') })
|

动态参数
获取通过冒号匹配的动态参数
使用req.params对象获取url中通过冒号匹配到的动态参数,默认是空对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const express = require('express')
const app = express()
app.get('/info/:id', (req, res) => { console.log(req.params) console.log(req.params.id) })
app.get('/info/:name/:age', (req, res) => { console.log(req.params) console.log(req.params.name) console.log(req.params.age) })
app.listen(8080, () =>{ console.log('listening on 8080') })
|


静态资源托管
静态资源服务器
使用app.use(express.static('public'))托管静态资源
- 在指定的目录中查找文件,并提供资源访问路径,存放静态资源的目录名不会出现在url中
1 2 3 4 5 6 7 8 9 10
| const express = require('express')
const app = express()
app.use(express.static('./'))
app.listen(8080, () =>{ console.log('listening on 8080') })
|
在浏览器中访问指定文件

注意
- 如果想要托管多个目录,那么多次调用
app.use(express.static(path))即可
- 在访问的时候,服务器会按照use的顺序查找文件
路径前缀
使用app.use(prefix, express.static(url)) 添加路径前缀
1 2 3 4 5 6 7 8 9 10 11 12 13
| const express = require('express')
const app = express()
app.use(express.static('./'))
app.use('/public', express.static('./'))
app.listen(8080, () =>{ console.log('listening on 8080') })
|

模块化路由
模块化路由
方便对路由进行模块化管理,不建议把路由直接挂载到app实例上,而是将路由拆分为单独的模块。
步骤:
- 创建模块对应的js文件
- 调用
express.Router()函数创建路由对象
- 将具体路由挂载到路由对象上
- 使用
module.expoerts导出共享路由对象
- 使用
app.usr注册路由模块
创建模块对应的js文件、调用express.Router()函数创建路由对象、将具体路由挂载到路由对象、使用module.expoerts导出共享路由对象
api/user.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| const express = require('express')
const router = express.Router()
router.get('/user/list', (req, res) => { res.send('Get User List') })
router.post('/user/add', (req, res) => { res.send('post User add') })
module.exports = router
|
使用app.usr注册路由模块
index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const express = require('express') const app = express()
const userRouter = require('./api/user')
app.use(userRouter)
app.listen(8080, ()=> { console.log('listening on 8080') })
|
添加访问前缀
在上一节中,我们的userAPI的有统一的前缀/user,在每一个user路由前面都写上是非常麻烦的,那么如何统一添加user路由前缀呢?
方法:
- 在注册的时候使用
app.use('/user', userRouter)
- 第一个参数为访问前缀,第二个参数为路由对象
api/user.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const express = require('express')
const router = express.Router()
let user = {"id":1,"name":"zhangsan", "age":18}
router.get('/list', (req, res) => { res.send(user) })
router.post('/add', (req, res) => { res.send('post User add') })
module.exports = router
|
index.js
1 2 3 4 5 6 7 8 9 10 11 12
| const express = require('express') const app = express()
const userRouter = require('./api/user')
app.use('/user', userRouter)
app.listen(8080, ()=> { console.log('listening on 8080') })
|
中间件
是什么
- 业务处理的中间环节,起到承上启下的作用
- express 的中间件的作用是对请求进行预处理
中间件的函数和路由极其相似,只不过,路由的形参列表只有req和res,而中间件的形参列表中必须有next
官方结构

next的作用是?
- 实现多个中间件连续调用的关键,表示把流转关系交给下一个路由或者中间件
基本使用
最简单的中间件
注意:
- 函数的参数必须带有next
- 在函数的最后必须调用next函数把流转关系交给下一个中间件或者路由
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const express = require('express')
const app = express()
const middleware = (req, res, next) => { console.log('最简单的中间件函数') next() }
app.listen(8080, ()=> { console.log('listening on 8080 ') })
|
全局生效的中间件
客户端的所有请求到达服务器后都会触发的中间件叫做全局生效的中间件
调用app.use(middleware_function)可以定义一个全局生效的中间件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| const express = require('express')
const app = express()
const middleware = (req, res, next) => { console.log('全局中间件函数') next() }
app.use(middleware)
app.get('/', (req, res) => { res.send('root') })
app.get('/test', (req, res) => { res.send('test') })
app.listen(8080, ()=> { console.log('listening on 8080 ') })
|

简化定义全局中间件:
1 2 3 4
| app.use((req, res, next) => { console.log('简化形式') })
|
中间件的作用
多个中间件之间可以共享同一份req或者res。
因此我们能在上游的中间件中统一给req或者res添加自定义的属性或者方法,供下游的中间件使用
获取请求到达服务器的时间
上游挂载获取时间的函数,下游访问请求到达服务器的时间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const express = require('express')
const app = express()
app.use((req, res, next) => { const time = Date.now() req.startTime = time next() })
app.get('/', (req, res) => { res.send('' + req.startTime) })
app.listen(8080, ()=> { console.log('listening on 8080 ') })
|
多个全局中间件
定义了多个中间件后,按照上下顺序进行调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| const express = require('express')
const app = express()
app.use((req, res, next) => { console.log('the first mw') next() })
app.use((req, res, next) => { console.log('2 mw') next() })
app.use((req, res, next) => { console.log('3 mw') next() })
app.get('/', (req, res) => { res.send('Hello') })
app.listen(8080, ()=> { console.log('listening on 8080 ') })
|
局部生效的中间件
不适用app.use定义的中间件都叫局部中间件
只针对某个路由生效,在需要生效的路由函数中作为第二个参数即可,也就是app.get('/', middlewareFunction, handler)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| const express = require('express')
const app = express()
const mw = (req, res, next) => { console.log('local mw') next() }
app.get('/', mw, (req, res) => { res.send('Hello') })
app.get('/test', (req, res) => { res.send('Hello') })
app.listen(8080, ()=> { console.log('listening on 8080 ') })
|
如果想要定义多个局部中间件,其余的局部中间件依次排列即可,也可以用列表包围
1 2 3 4 5 6 7 8 9
| app.get('/multi', [mw, mw, mw, mw, mw, mw], (req, res) => { res.send('Hello') })
app.get('/multi', mw, mw, mw, mw, mw, mw, (req, res) => { res.send('Hello') })
|
注意
- 在路由前面定义中间件
- 链式调用
- 记住
next(),并且在next()后面不要再写别的代码
- 多个中间件共享req和res对象
中间件的分类
应用级中间件
- 通过
app.use()或者app.get(),app.post()绑定到app上的中间件
路由级中间件
- 绑定到
express.Router()实例上的中间件,使用router.use进行注册的中间件
- 和应用级中间件在用法上没有任何区别
错误级中间件
错误级别的中间件一定要注册到所有的路由之后,这样才能捕获到所有的错误
捕获项目中发生的异常错误,防止项目一场崩溃的问题
格式:中间件的处理函数上必须写上4个形参 app.use(function(err, req, res, next)=>{}),必须写上err
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| const express = require('express')
const app = express()
app.get('/', (req, res) =>{ throw new Error("error")
res.send('after throw error') })
app.use((err, req, res, next) => { console.log('error occured...') res.send('error page') next() })
app.listen(8080, ()=> { console.log('listening on 8080 ') })
|
Express 内置中间件(3个)
express.static()快速托管静态资源(无兼容性)
app.use(express.ststic()) 上面讲了
express.json()解析json格式的请求体数据(4.16.0+)
app.use(express.json())
express.urlencoded解析url-encoded格式的请求体数据(4.16.0+)
app.use(express.urlencoded({extended: false }))
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| const express = require('express')
const app = express()
app.use(express.json())
app.post('/user', (req, res) =>{ console.log(res.body) res.send('insert success') })
app.listen(8080, ()=> { console.log('listening on 8080 ') })
|

1 2 3 4 5 6 7 8 9 10 11
|
app.use(express.urlencoded( {extended: false} ))
app.post('/students', (req, res) =>{ console.log(req.body) res.send('student insert success') })
|
第三方中间件
body-parser中间件的使用 npm install body-parser
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const express = require('express')
const parser = require('body-parser')
const app = express()
app.use(parser.urlencoded({extended: false}))
app.post('/user', (req, res) => { console.log(req.body) })
app.listen(8080, ()=> { console.log('listening on 8080 ') })
|
- express内置的express.urlencoded这个中间件是基于body-parser封装出来的,因此用法极其相似
自定义中间件
模拟一个类似于express.urlencoded这样的中间件解析POST提交到服务器的数据
步骤
- 定义中间件
- 监听req的data事件
- 监听req的end事件
- 使用querystreing模块解析数据
- 将解析出来的中间件挂载到req.body
- 将自定义中间件封装为模块
基于Express的接口