express
是一个基于node.js
的web应用框架,它提供了一系列强大的特性,帮助你创建各种web和移动设备应用。express
是一个轻量级的包含路由系统的web框架,它没有内置的中间件,但是它提供了一系列的中间件。
安装
npm install express --save
快速入门
var express = require('express');
var app = express();
// 注册一个路由
app.get('/', function (req, res) {
res.send('Hello World!');
});
// 启动服务,监听3000端口
app.listen(3000, function () {
console.log('app is listening at port 3000');
});
认识中间件
中间件是一个函数,它可以访问请求对象(request object (req)
),响应对象(response object (res))
,和web应用中处于请求-响应循环流程中的中间件,一般被命名为next
的变量。
中间件的功能包括:
- 执行任何代码。
- 修改请求和响应对象。
- 终结请求-响应循环。
- 调用堆栈中的下一个中间件。
- 如果当前中间件没有终结请求-响应循环,则必须调用
next()
方法将控制权交给下一个中间件,否则请求就会挂起。
/* 省略引用 express 的代码 */
app.use(function (req, res, next) {
console.log('Time:', Date.now());
next();
});
/* 省略路由注册代码和服务启动代码 */
上面的代码中,我们定义了一个中间件,它会在每次请求时打印出当前时间,然后调用next()
方法将控制权交给下一个中间件,我们这里只有一个中间件,所以当请求到来时,它会打印出当前时间,然后返回Hello World!
。
注册中间件时,参数不仅可以是一个函数,还可以是一个包含多个函数的数组,这样就可以注册多个中间件。
function fn(req, res, next) {
console.log('Time:', Date.now());
next();
}
// 可以是单个函数
app.use(fn);
// 可以是多个参数的函数
app.use(fn, fn);
// 也可以是函数组成的数组
app.use([fn, fn]);
中间件的第一个参数还可以是一个字符串,用来标识一个路径,只有请求的路径匹配了该字符串,才会执行该中间件。
app.use('/user', function (req, res, next) {
console.log(1);
next();
});
上面的代码中,我们定义了一个中间件,它只有在请求的路径是/user
时才会执行,其他的请求地址都不会执行该中间件。
中间件函数的参数
中间件函数的参数分别是request对象
、response对象
、next函数
和err对象
,它们的作用分别是:
request对象
:表示HTTP请求,包含了请求查询字符串、参数、内容、HTTP头部等属性。response对象
:表示HTTP响应,该对象的方法用于发送HTTP响应。next函数
:是express
框架中的核心,表示执行下一个中间件。err对象
:表示错误对象,如果中间件中调用了next()方法并传入了一个参数,则表示发生了错误,参数就是错误对象。
req
、res
和next
这三个参数是必须的,如果定义的中间件函数不需要使用这三个参数,那么就可以省略它们。
err对象
是可选的,如果中间件函数中没有发生错误,则可以省略它。
如果中间件函数中发生了错误,则必须调用next()
方法,并将错误对象作为参数传入,否则后续的中间件就不会执行了。
如果中间件函数中没有发生错误,则必须调用next()
方法,否则后续的中间件就不会执行了。
如果中间件函数中调用了next()
方法,并且传入了参数,则后续的中间件就不会执行了。
注意:如果中间件函数中没有调用
next()
方法,则后续的中间件就不会执行了。
中间件的分类
中间件可以分为应用级中间件和路由级中间件。
应用级中间件
应用级中间件绑定到app对象使用,它的路径是/
,即任何路径都可以匹配到。
app.use('/', function (req, res, next) {
console.log('Time:', Date.now());
next();
});
上面的代码中,我们定义了一个应用级中间件,它会在每次请求时打印出当前时间。
路由级中间件
路由级中间件绑定到express.Router()
实例上,它的路径是/,即当前Router
实例中的任何路径都可以匹配到。
var express = require('express');
var app = express();
var router = express.Router();
router.use(function (req, res, next) {
console.log('Time:', Date.now());
next();
});
router.get('/', function (req, res) {
res.send('Hello World!');
});
app.use('/', router);
app.listen(3000, function () {
console.log('app is listening at port 3000');
});
上面的代码中,我们定义了一个路由级中间件,它会在每次请求时打印出当前时间。
路由级中间件的路径是/
,即任何路径都可以匹配到,因此,它会在每次请求时打印出当前时间。
也可以为路由级中间件指定一个路径(所有的中间件都可以指定,不仅仅是路由的),这样它就只有在指定的路径才会生效。
var express = require('express');
var app = express();
var router = express.Router();
router.use('/user/:id', function (req, res, next) {
console.log(1);
next();
});
router.get('/user/:id', function (req, res, next) {
res.send(req.params.id);
});
app.use('/', router);
app.listen(3000, function () {
console.log('app is listening at port 3000');
});
中间件的执行顺序
中间件的执行顺序是按照它们的定义顺序来执行的。
app.use(function (req, res, next) {
console.log(1);
next();
});
app.use(function (req, res, next) {
console.log(2);
next();
});
上面的代码中,我们定义了两个中间件,它们的定义顺序是先后的,因此它们的执行顺序也是先后的,所以每次调用接口时,控制台会依次打印出1和2。
中间件的示例
在使用express
框架开发Web应用时,没有任何一个开发者离得开中间件,例如json解析中间件
、cookie解析中间件
、session中间件
、静态资源中间件
等等。
json解析中间件
express
框架内置了一个json解析中间件
,可以解析请求体中的json
数据。
app.use(express.json());
cookie解析中间件
express
框架内置了一个cookie解析中间件
,可以解析请求头中的cookie
数据。
app.use(express.cookieParser());
session中间件
express
框架内置了一个session中间件
,可以解析请求头中的session
数据。
app.use(express.session());
静态资源中间件
express
框架内置了一个静态资源中间件
,可以解析请求头中的静态资源数据。
app.use(express.static());
自己实现一个中间件
中间件的本质就是一个函数,只要会写函数,就可以写出一个中间件,现在我们来实现一个用户登录拦截的中间件。
var express = require('express');
var app = express();
app.use(function (req, res, next) {
// 判断白名单地址(不需要登录就可以访问的接口)
const whiteList = ['/login', '/register'];
if (whiteList.includes(req.path)) {
next();
return;
}
// 判断用户是否登录
if (req.session.user) {
next();
return;
}
// 用户未登录,跳转到登录页面
res.redirect('/login');
});
app.get('/login', function (req, res) {
res.send('登录成功');
});
app.listen(3000, function () {
console.log('app is listening at port 3000');
});
简简单单我们就实现了一个用户登录拦截的中间件,不过功能还是比较简单的,如果系统足够复杂,例如需要对用户的权限进行判断,那么中间件的功能就会变得非常复杂,这时候,我们就需要将中间件拆分成多个小的中间件,然后再组合起来,这样就可以实现中间件的复用。
总结
本文主要介绍了中间件的概念,以及中间件的执行顺序,最后还实现了一个用户登录拦截的中间件。
总体来说中间件的使用还是比较简单的,但是如果要写好一个中间件,还是需要一定的经验的,因为中间件的功能非常复杂,如果不是很熟悉,很容易就会写出一个很复杂的中间件,这样就会导致中间件的可维护性变差,所以在写中间件的时候,一定要注意中间件的可维护性。