1.示例
服务器在接收到不同get请求之后,打印get请求的日志
const express = require('express')
const app = express()
// 中间件的顺序很重要
// req 请求对象 res 响应对象
// next 下一个中间件
app.use((req, res, next) => {
console.log(req.method, req.url, Date.now())
// 交出执行权,往后继续匹配执行
next()
})
app.get('/', (req,res) => {
res.send('get/')
})
app.get('/login', (req,res) => {
res.send('get/login')
})
app.get('/about', (req,res) => {
res.send('get/about')
})
app.listen(3000, () => {
console.log(`Server running at http://localhost:3000/`)
})
2.概念解析
Express的最大特色,也是最重要的一个设计,就是中间件。中间件就是一个可以访问请求对象、响应对象和调用next方法的一个函数。
Express中间件和AOP(Aspect Oriented Programming)一样,就是都需要经过一些步骤来扩展或处理一些功能,但是不去修改自己的代码,不影响原有功能
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率和可维护性
注意:如果当前的中间件功能没有结束请求-响应周期,则必须调用next()方法将控制权交给下一个中间件功能。否则,该请求将被挂起。
3.中间件分类
应用程序级别中间件
const express = require('express')
const app = express()
...
app.listen(3000, () => {
console.log(`Server running at http://localhost:3000/`)
})
①不关心请求路径
app.use(function(req, res, next) {
console.log('Time:', Date.now())
next()
})
②限定请求路径
app.use('/user/:id', function(req, res, next) {
console.log('Request Type:', req.method)
next()
})
③限定请求方法+请求路径
app.get('/user/:id', function(req, res, next) {
res.send('USER')
})
④多个处理函数
app.use('/user/:id', function(req, res, next) {
console.log('Request URL:', req.originalUrl)
next()
}, function (req, res, next) {
console.log('Request Type:', req.method)
next()
})
⑤为同一个路径定义多个处理中间件
app.get('/user/:id', function(req, res, next) {
console.log('ID:', req.params.id)
next()
}, function (req, res, next) {
res.send('User Info')
})
app.get('/user/:id', function(req, res, next) {
res.end(req.params.id)
})
因为send()已经结束了响应,所以后面的 res.end(req.params.id) 无效了
⑥要从路由器中间件堆栈中跳过其余中间件功能,要使用next('route')将控制权传递给下一条路由
app.get('/user/:id', function(req, res, next) {
if (req.params.id === '0') next('route')
else next()
}, function (req, res, next) {
res.send('regular')
})
app.get('/user/:id', function(req, res, next) {
res.send('special')
})
⑦中间件也可以在数组中声明为可重用
function logOriginalUrl (req, res, next) {
console.log('Request URL:', req.originalUrl)
next()
}
function logMethod (req, res, next) {
console.log('Request Type:', req.method)
next()
}
var logStuff = [logOriginalUrl, logMethod]
app.get('/users/:id', logStuff, function(req, res, next) {
res.end('User Info')
})
路由级别中间件
对上面的一个小案例进行修改,将路由部分进行一个封装
router.js
// 路由模块
const express = require('express')
const { getDb, saveDb } = require('./db')
// 1.创建路由实例,路由实例就相当于一个mini Express实例
const router = express.Router()
// 2.配置路由
router.get('/', async (req,res) => {
... //原代码不变
})
router.get('/:id', async (req,res) => {
... //原代码不变
})
router.post('/', async (req,res) => {
... //原代码不变
})
// 3. 导出路由实例
module.exports = router
app.js
const express = require('express')
const router = require('./router')
const app = express()
// 解析表单请求体:application/json
app.use(express.json())
// 解析表单请求体:application/x-www-form-urlencoded
app.use(express.urlencoded())
// 挂载路由,并给路由限定访问前缀,不限定前缀:app.use(router)
app.use('/todos', router)
app.listen(3000, () => {
console.log(`Server running at http://localhost:3000/`)
})
错误处理中间件
在上面的基础上修改
router.js
将任何内容传给next()函数('route'除外),Express都会将当前请求视为错误,并且将跳过所有剩余的非错误处理路由和中间件函数,也就是会来到错误处理中间件。
// 路由模块
const express = require('express')
const { getDb, saveDb } = require('./db')
// 1.创建路由实例,路由实例就相当于一个mini Express实例
const router = express.Router()
// 2.配置路由
router.get('/', async (req,res,next) => {
try {
... //原代码不变
} catch(err) {
next(err)
}
})
router.get('/:id', async (req,res,next) => {
try {
... //原代码不变
} catch (err) {
next(err)
}
})
router.post('/', async (req,res,next) => {
try {
... //原代码不变
} catch(err) {
next(err)
}
})
// 3. 导出路由实例
module.exports = router
app.js
我这里顺便处理了请求内容找不到的404错误:在所有的路由之后配置
const express = require('express')
const router = require('./router')
const app = express()
// 解析表单请求体:application/json
app.use(express.json())
// 解析表单请求体:application/x-www-form-urlencoded
app.use(express.urlencoded())
// 挂载路由,并给路由限定访问前缀,不限定前缀:app.use(router)
app.use('/todos', router)
// 通常在所有的路由之后配置处理404内容
app.use((req,res,next) => {
res.status(404).send('404 Not Found.')
})
// 在所有中间件之后挂载错误处理中间件
// 这里想用err就必须有4个参数
app.use((err, req, res, next) => {
console.log('错误', err)
res.status(500).json({
error: err.message
})
})
app.listen(3000, () => {
console.log(`Server running at http://localhost:3000/`)
})
内置中间件
express.json() 解析Content-Type为application/json格式的请求体
express.urlencoded() 解析Content-Type为application/x-www-form-urlencoded格式的请求体
express.json() 解析Content-Type为application/octet-stream格式的请求体
express.text() 解析Content-Type为text/plain格式的请求体
express.static() 托管静态资源文件
第三方中间件
具体有哪些以及怎么用可查看官网
这里以morgan为例,该中间件是用来打印日志的
先安装 npm install morgan,具体用法如下
app.js
const express = require('express')
const morgan = require('morgan')
const app = express()
app.use(morgan(':method :url :status :res[content-length] - :response-time ms'))
app.get('/', (req,res) => {
res.send('get /')
})
app.listen(3000, () => {
console.log(`Server running at http://localhost:3000/`)
})