从“请求-响应循环”理解Express中间件,通俗易懂!
在接触Express
中间件时,发现许多博客和资料上来就是列概念和做案例,普遍存在用2*2
去解释1+1
的问题。如标题所说,在学习中间件(Middleware
)的概念之前,非常有必要先了解一下请求-响应循环(Request-Response Cycle
)。因为它其实是Express
开发的根本所在。
请求—响应循环
不要被这个名字唬到了,请求-响应循环其实就是客户端向服务端发送网络请求,并接收响应的过程。因为那个经典的面试问题——输入url
😮,这个过程我们再熟悉不过了,但它说复杂也再复杂不过了。不过那些都不是我们现在要关注的,我们关注的是Express
在其中的作用,准确说是中间件在其中扮演了什么角色。我们通过下面这张图来理解:
简单来说,中间件就是在请求-响应的过程中给我们提供一个可以修改内容的机会。中间件的这个中间说的是请求(
Request
)和响应(Response
)的中间,从功能上看,中间件就是一个可以处理请求和响应内容的函数。而Express
应用程序本质上就是一系列中间件函数的调用过程,维系着这个调用的是每个中间件内的next()
函数。
中间件堆栈
以上涉及的所有中间件维持着一个中间件堆栈(Middleware Stack
),它们的执行顺序完全取决于在代码中定义时的顺序,首先在代码中出现的中间件将首先执行。也就是说,整个执行过程其实是线性的。
现在我们来梳理一下Express
中从发出请求到返回响应的整个过程:
首先,Express
会构建出request
和response
对象;之后,这俩对象会遍历每个程序中的每个中间件,在每个中间件中它们可能会被处理或者执行其它的代码,在每个中间件的最后会调用next()
函数来将控制权交给下一个中间件,最后一个中间件通常是处理路由(router
)的,因此没有调用下一个中间件,而是将响应数据返回给客户端。最终,完成了一次请求—响应循环。
我们可以将上述过程看作一个数据从请求传输到最终响应的管道(Pipeline)。这样就更直观了。
最小实践
我们使用app.use
方法来在Express
中注册中间件,在之前的文章中,我们使用Express
发送POST
请求时,就用到了app.use(express.json())
来创建一个最普通的中间件,通过app.use
注册的中间件,无论请求方式是什么,请求路径是什么,它都会被执行。事实上在Express
中所有东西都是中间件,比如app.get()
、app.post()
也只是针对特定的请求方式和路径。
app.use
接收req
, res
和next
三个参数,在app.js
中输入以下代码:
// app.js
const express = require('express');
const app = express();
app.use((req, res, next) => {
console.log('middleware 1');
next();
});
app.use((req, res, next) => {
console.log('middleware 2');
next();
});
app.listen(3000, () => {
console.log('App running at port 3000');
});
在Postman
我中使用任意一种方式向127.0.0.1:3000
发送请求,可以看到控制台输出如下:
下面对代码做些改动:
app.use((req, res, next) => {
console.log('middleware 1');
next();
});
app.use((req, res, next) => {
console.log('middleware 2');
next();
});
app.get('/', (req, res) => {
console.log('middleware get');
res.send('Hello from server');
});
此时控制台的输出:
可以看到app.get()
方法本身就是中间件,只是应用于特定的URL
。而使用app.use()
所创建的中间件响应所有请求,因为它们是在含有res.send()
方法的中间件之前定义的。
我们可以调整一下代码的顺序:
app.use((req, res, next) => {
console.log('middleware 1');
next();
});
app.get('/', (req, res) => {
console.log('middleware get');
res.send('Hello from server');
});
app.use((req, res, next) => {
console.log('middleware 2');
next();
});
由于app.get()
中使用了res.send()
来结束127.0.0.1:3000
这个URL
的请求—响应循环,所以第二个中间件不会被执行。
转载自:https://juejin.cn/post/7158479306774020110