express 中间件

背景

中间件的应用十分广泛。

场景1:我们需要记录每个接口调用时间时,就可以使用中间件函数。使用中间件函数后,在调用每个接口时,在不修改之前接口代码的基础上,可以自动打印对应的时间。
场景2 路由也是中间件:只不过路由会限制对中间件的规则有限制。

常用中间件

  • 打印接口调用时间
  • 打印接口调用ip
  • 打印接口耗时
app.use(async (req, res, next)=>{
    let startTime = Date.now();
    console.log("ip:" + req.ip + "------- interface:" + req.originalUrl + "----- time:" + currentTime)
    // 把流转关系转交给下一个中间件或者路由
    next()
    let endTime = Date.now()
    console.log(req.originalUrl + "-------- 执行时间:" + (endTime - startTime)/1000 + "s")
})

中间件

Express中间件和AOP面向切面编程是一个意思,不需要修改自己的代码,以此来扩展或者处理一些功能。

将日志记录,性能统计,安全控制,事务处理, 异常处理等代码从业务逻辑代码 中划分出来,通过对这些行为的分离,我们希望望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业上务逻辑的代码。

  • 当一个请求到达Express的服务器后,可以连续调用多个中间件,从而对这次请求进行预处理。
  • 中间件函数的形参列表中,必须包含next参数。而路由处理函数中只能包括req以及res。
  • next()函数是实现多个中间件连续调用的关键,他表示把流转关系交给下一个中间件或路由。
  • 多个中间件,共用一个req、res。
  • 请求到来之后按照代码从上到下的顺序进行匹配,因此中间件需要在路由之前声明。

中间件可实现功能

1、执行任何代码
2、修改request或者response响应对象
3、结束请求相应周期
4、调用下一个中间件

如果从路由器中间件堆栈跳过其余中间件功能,请调用next('route')将控制权传递给下一个路由。
注意:next('route')仅在app.METHOD()router.METHOD()函数加载的中间件函数中有效。

中间件的使用

const express = require("express");

const app = express()

// 定义一个简单的中间件 常量mw所指
const mw = function (req, res, next) {
    console.log("这种最简单的中间件函数")
    // 把流转关系转交给下一个中间件或者路由
    next()
}

app.use(mw)

app.get('/',(req, res)=>{
    console.log('Home page')
    res.send("Home page")
})
app.get('/user',(req, res)=>{
    console.log('user page')
    res.send("user page")
})


app.listen(8080,()=>{
    console.log("express start on 8080")
})

请求结果:

这种最简单的中间件函数 
Home page 

中间件的简化生成

中间件的调用可以这样简化:

const express = require("express");

const app = express()

// 定义全局中间件的简化形式
app.use((req, res, next)=>{
    console.log("简化的全局中间件...")
    next()
})

app.get('/',(req, res)=>{
    console.log('Home page')
    res.send("Home page")
})
app.get('/user',(req, res)=>{
    console.log('user page')
    res.send("user page")
})


app.listen(8080,()=>{
    console.log("express start on 8080")
})

中间件挂载方法或者属性

const express = require("express");

const app = express()

/**
 * 中间件的上游挂载属性,下游可以访问到挂载的属性。
 */
// 定义全局中间件的简化形式
app.use((req, res, next)=>{
    console.log("简化的全局中间件...")
    /**
     * 为req对象 挂载自定义属性(接受请求的时间),从而把时间共享给后面的所有路由
     */
    req.startTime = new Date()
    next()
})

app.get('/',(req, res)=>{
    console.log('Home page')
    res.send("Home page " + req.startTime)
})
app.get('/user',(req, res)=>{
    console.log('user page')
    res.send("user page  " + req.startTime)
})


app.listen(8080,()=>{
    console.log("express start on 8080")
})

多个中间件

express定义多个中间件时,会按照定义的先后顺序在多个中间件间进行信息的流转。

const express = require("express");

const app = express()

/**
 * 中间件的上游挂载属性,下游可以访问到挂载的属性。
 * 定义全局中间件的简化形式
 */
app.use((req, res, next)=>{
    console.log("简化的全局中间件...")
    /**
     * 为req对象 挂载自定义属性(接受请求的时间),从而把时间共享给后面的所有路由
     */
    req.startTime = new Date()
    next()
})

/**
 * 第二个全局中间件
 * 如果挂载多个全局中间件,express会按照定义的顺序前后调用多个中间件
 */
app.use((req, res, next)=>{
    console.log("第二个中间件")
    next()
})


app.get('/',(req, res)=>{
    console.log('Home page')
    res.send("Home page " + req.startTime)
})
app.get('/user',(req, res)=>{
    console.log('user page')
    res.send("user page  " + req.startTime)
})


app.listen(8080,()=>{
    console.log("express start on 8080")
})

打印的日志信息如下:

express start on 8080 
简化的全局中间件... 
第二个中间件 
Home page

局部生效的中间件

const express = require("express");

const app = express()

// 定义一个简单的中间件 常量mw所指
const mw = function (req, res, next) {
    console.log("调用了局部生效的中间件")
    // 把流转关系转交给下一个中间件或者路由
    next()
}

app.get('/',mw,(req, res)=>{
    console.log('Home page')
    res.send("Home page " + req.startTime)
})
app.get('/user',(req, res)=>{
    console.log('user page')
    res.send("user page  " + req.startTime)
})


app.listen(8080,()=>{
    console.log("express start on 8080")
})


多个局部生效的中间件

使用多个局部中间件的两种方式
app.get(‘/’, mw, mw2, (req, res)
app.get(‘/user’, [mw, mw2], (req, res)

const express = require("express");

const app = express()

// 定义一个简单的中间件 常量mw所指
const mw = function (req, res, next) {
    console.log("调用了局部生效的中间件")
    // 把流转关系转交给下一个中间件或者路由
    next()
}

// 定义一个简单的中间件 常量mw所指
const mw2 = function (req, res, next) {
    console.log("调用了第二个局部生效的中间件")
    // 把流转关系转交给下一个中间件或者路由
    next()
}

/**
 * 使用多个局部中间件的两种方式
 * app.get('/', mw, mw2, (req, res)
 * app.get('/user', [mw, mw2], (req, res)
 */
app.get('/', mw, mw2, (req, res) => {
    console.log('Home page')
    res.send("Home page " + req.startTime)
})
app.get('/user', [mw, mw2], (req, res) => {
    console.log('user page')
    res.send("user page  " + req.startTime)
})


app.listen(8080, () => {
    console.log("express start on 8080")
})

中间件分类

应用级别的中间件

通过app.use()(全局)或者app.get()(局部),绑定到app实例上的中间件,叫做应用级别的中间件。

路由级别的中间件

绑定到express.Router()实例上的中间件,叫做路由级别的中间件。他的用法和应用级别中间件没有任何区别。只不过,应用级别中间件时绑定到app实例上,路由级别中间件绑定到router实例上。

错误级别的中间件

错误级别的中间件,必须有四个形参,err req res next
错误级别中间件,一定要注册在所有的路由之后。

const express = require('express');

const app = express();

// 不添加错误路由的情况下 人为添加错误
app.get("/", (req, res) => {
    throw new Error('服务器内部发生了错误')
    res.send(' Home page')
})
// 创建错误级别的中间件,捕获整个项目中的异常错误,从而防止整个项目的崩溃
app.use((err, req, res, next) => {
    console.log("发生了错误:" + err.message)
    res.send("error:" + err.message)
})

app.listen(8080, () => {
    console.log(' 启动在8080  ')
})

404中间件

通常在所有的路由之后配置处理404的内容。
请求来到后匹配所有的中间件以及所有的路由,然后发现客户端网址对应的地址无法匹配,进而进入404处理环节,返回404。因此404中间件需要放在所有的中间件(非错误中间件)以及路由之后。

Express内置的中间件


> express.static 
> express.json(4.16.0+) 
> express.urlencoded(4.16.0+)

express.json()中间件的使用

const express = require('express');

const app = express();

// 除了错误级别的中间件,其他的中间件,必须在路由之前进行配置
//  通过express.json()这个中间件 解析表单中json格式的请求体数据,
app.use(express.json())

app.post('/user',(req, res)=>{
    // 在服务器,可以通过req.body这个属性,来接收客户端发送过来的请求体数据
    // 默认情况下,如果不配置解析表单数据的中间件,则req.body 默认等于 undefined
    console.log(req.body)
    res.send(' ok')
})

app.listen(8080, () => {
    console.log(' 启动在8080  ')
})

express.urlencoded()中间件的使用

const express = require('express');
const app = express();
// 通过express.urlencoded()这个中间件 解析表单中的url-encoded格式的数据
app.use(express.urlencoded())

app.post('/user',(req, res)=>{
    // 在服务器,可以通过req.body这个属性,来接收客户端发送过来的请求体数据
    // 默认情况下,如果不配置解析表单数据的中间件,则req.body 默认等于 undefined
    console.log(req.body)
    res.send(' ok')
})

app.listen(8080, () => {
    console.log(' 启动在8080  ')
})

第三方的中间件

body-parser

const express = require('express');
const parser = require('body-parser');
const app = express();
// 除了错误级别的中间件,其他的中间件,必须在路由之前进行配置
// 注意express 内置的express.urlencoded中间件 就是基于body-parser这个中间件进一步封装出来的
app.use(parser.urlencoded({extended : false}))
app.post('/user',(req, res)=>{
    // 在服务器,可以通过req.body这个属性,来接收客户端发送过来的请求体数据
    // 默认情况下,如果不配置解析表单数据的中间件,则req.body 默认等于 undefined
    console.log(req.body)

    res.send(' ok')
})
app.listen(8080, () => {
    console.log(' 启动在8080  ')
})

自定义中间件

import qs from "querystring";

const bodyParser = (req,res,next)=>{
    // 监听req的data事件,来获取客户端发送到服务端的数据
    // 如果数据量比较大,无法一次发送完毕,客户端会将数据分割后,分批发送到服务器,所以data事件可能会触发多次。每触发
    //一次,获取到数据只是完整数据的一部分,需要手动对接收到的数据进行拼接。
    let str = ''
    req.on('data',(chunk)=>{
        str += chunk
    })
    // 当请求体数据接收完毕之后,会自动触发req的end事件
    // 因此,我们可以在req的end事件中,
    req.on('end',()=>{
        console.log(str)
        const body = qs.parse(str);
        console.log(body)
        // 将解析出来的数据对象挂载为req.body
        req.body = body
        next()
    })
}

module.exports = bodyParser;

Express实现get post请求

创建express()类,使用router路由

const express = require("express");
const app = express()

app.use(express.urlencoded({extended : false}))

const router = require("./9")
app.use('/user',router)

app.listen(8080,()=>{
    console.log("server start at 8080")
})


路由中,声明使用get请求

const express = require('express')
const router = express.Router()

router.get('/get',(req, res)=>{
    // 通过req.query获取客户端通过查询字符串,发送到服务器的数据
    let query = req.query;
    // 调用res.send() 向服务端响应处理的结果
    res.send({
        status : 0,
        msg : ' GET请求成功',
        data: query
    })
})


router.post('/post',(req, res)=>{
    // 获取客户端发送到服务端的url-encoded数据
    let body = req.body;

    // 调用res.send() 把数据响应给客户端
    res.send({
        status : 0,
        msg : ' Post 请求成功',
        data: body
    })
})

module.exports = router

跨域

需要在路由之前配置cors中间件,从而解决接口跨域的问题

const express = require("express");
const app = express()
var cors = require('cors');


/**
 * 跨域问题的方案
 * 1. cors 主流的解决方案 推荐使用
 * 需要在路由之前配置cors中间件,从而解决接口跨域的问题
  */
app.use(cors())

// 配置中间件,才可以拿到urlencoded类型的数据
app.use(express.urlencoded({extended : false}))

const router = require("./9")
app.use('/user',router)

app.listen(8080,()=>{
    console.log("server start at 8080")
})

cors响应头分三类

1.assess-control-allow-origin 可以控制允许哪些网站的跨域请求。
2. 默认情况下,cors仅支持客户端向服务端发送的9个请求头,如果客户端向服务端发送了额外的请求头信息,则需要在服务端,通过Assess-Control-Allow-Headers对额外的请求头进行声明,否则这次请求会失败。 多个请求头之前用英文逗号分割。
3. Assess-Control-Allow-Methods 默认情况下,cors仅支持客户端发起GET、Post、Head请求。如果希望支持Put、Delete等方式请求服务器的资源。则需要在服务器端指明。

cors响应头分三类

简单请求。

1.没有用到自定义的cors响应头。
客户端与服务器只会发生一次请求。

预检请求。

1.使用了自定义的cors响应头。
客户端与服务器之间会发生两次请求。OPTIONS预检成功后,才会发送真正的请求。

express

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学知识拯救世界

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值