中间件概念 #

一、什么是中间件? #

1.1 定义 #

中间件(Middleware)是Express的核心概念。中间件函数是可以访问请求对象(req)、响应对象(res)和应用程序请求-响应循环中下一个中间件函数的函数。

1.2 中间件函数结构 #

javascript
function middleware(req, res, next) {
    
}
参数 说明
req 请求对象
res 响应对象
next 调用下一个中间件的函数

1.3 基本示例 #

javascript
const express = require('express');
const app = express();

app.use((req, res, next) => {
    console.log('这是一个中间件');
    next();
});

app.get('/', (req, res) => {
    res.send('Hello World');
});

app.listen(3000);

二、中间件工作原理 #

2.1 请求处理流程 #

text
请求 → 中间件1 → 中间件2 → 中间件3 → 路由处理 → 响应

每个中间件可以:

  1. 执行任何代码
  2. 修改请求和响应对象
  3. 结束请求-响应循环
  4. 调用下一个中间件

2.2 执行顺序 #

javascript
app.use((req, res, next) => {
    console.log('中间件1 - 开始');
    next();
    console.log('中间件1 - 结束');
});

app.use((req, res, next) => {
    console.log('中间件2');
    next();
});

app.get('/', (req, res) => {
    console.log('路由处理');
    res.send('Hello');
});

输出顺序:

text
中间件1 - 开始
中间件2
路由处理
中间件1 - 结束

2.3 next()的作用 #

javascript
app.use((req, res, next) => {
    console.log('中间件A');
    next();
    console.log('中间件A - next之后');
});

app.use((req, res, next) => {
    console.log('中间件B');
    next();
});

app.get('/', (req, res) => {
    res.send('Hello');
});

重要: next() 调用后,当前中间件代码会暂停,等待后续中间件执行完毕后再继续执行。

2.4 不调用next() #

javascript
app.use((req, res, next) => {
    res.send('请求在这里被拦截');
});

app.get('/', (req, res) => {
    res.send('这段代码永远不会执行');
});

如果不调用 next(),请求-响应循环会在此终止。

三、中间件类型 #

3.1 应用级中间件 #

使用 app.use()app.METHOD() 绑定到app实例:

javascript
app.use((req, res, next) => {
    console.log('应用级中间件');
    next();
});

app.get('/users', (req, res, next) => {
    console.log('路由特定中间件');
    next();
}, (req, res) => {
    res.json({ users: [] });
});

3.2 路由级中间件 #

使用 router.use() 绑定到router实例:

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

router.use((req, res, next) => {
    console.log('路由级中间件');
    next();
});

router.get('/users', (req, res) => {
    res.json({ users: [] });
});

module.exports = router;

3.3 错误处理中间件 #

有四个参数的中间件:

javascript
app.use((err, req, res, next) => {
    console.error(err.stack);
    res.status(500).json({ error: '服务器错误' });
});

3.4 内置中间件 #

Express内置的中间件:

javascript
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(express.static('public'));

3.5 第三方中间件 #

javascript
const morgan = require('morgan');
const cors = require('cors');
const helmet = require('helmet');

app.use(morgan('dev'));
app.use(cors());
app.use(helmet());

四、中间件挂载方式 #

4.1 全局挂载 #

javascript
app.use((req, res, next) => {
    console.log('所有请求都会经过这里');
    next();
});

4.2 路径挂载 #

javascript
app.use('/api', (req, res, next) => {
    console.log('只处理/api开头的请求');
    next();
});

4.3 路由特定中间件 #

javascript
app.get('/users', 
    (req, res, next) => {
        console.log('中间件1');
        next();
    },
    (req, res, next) => {
        console.log('中间件2');
        next();
    },
    (req, res) => {
        res.json({ users: [] });
    }
);

4.4 中间件数组 #

javascript
const middleware1 = (req, res, next) => {
    console.log('中间件1');
    next();
};

const middleware2 = (req, res, next) => {
    console.log('中间件2');
    next();
};

app.get('/protected', [middleware1, middleware2], (req, res) => {
    res.send('受保护的内容');
});

五、常见中间件模式 #

5.1 日志记录 #

javascript
app.use((req, res, next) => {
    const start = Date.now();
    
    res.on('finish', () => {
        const duration = Date.now() - start;
        console.log(`${req.method} ${req.url} ${res.statusCode} - ${duration}ms`);
    });
    
    next();
});

5.2 请求计时 #

javascript
app.use((req, res, next) => {
    req.startTime = Date.now();
    next();
});

app.get('/', (req, res) => {
    const duration = Date.now() - req.startTime;
    res.send(`请求处理时间: ${duration}ms`);
});

5.3 请求ID #

javascript
const uuid = require('uuid');

app.use((req, res, next) => {
    req.id = uuid.v4();
    res.set('X-Request-Id', req.id);
    next();
});

5.4 认证检查 #

javascript
const authMiddleware = (req, res, next) => {
    const token = req.headers['authorization'];
    
    if (!token) {
        return res.status(401).json({ error: '未授权' });
    }
    
    try {
        const decoded = verifyToken(token);
        req.user = decoded;
        next();
    } catch (error) {
        res.status(401).json({ error: '无效的token' });
    }
};

app.get('/protected', authMiddleware, (req, res) => {
    res.json({ user: req.user });
});

5.5 权限检查 #

javascript
const checkRole = (...roles) => {
    return (req, res, next) => {
        if (!req.user) {
            return res.status(401).json({ error: '未登录' });
        }
        
        if (!roles.includes(req.user.role)) {
            return res.status(403).json({ error: '无权限' });
        }
        
        next();
    };
};

app.delete('/users/:id', authMiddleware, checkRole('admin'), (req, res) => {
    res.json({ message: '删除成功' });
});

5.6 数据验证 #

javascript
const validateUser = (req, res, next) => {
    const { name, email, password } = req.body;
    const errors = [];
    
    if (!name) errors.push('用户名不能为空');
    if (!email) errors.push('邮箱不能为空');
    if (!password || password.length < 6) errors.push('密码至少6个字符');
    
    if (errors.length > 0) {
        return res.status(400).json({ errors });
    }
    
    next();
};

app.post('/users', validateUser, (req, res) => {
    res.status(201).json(req.body);
});

5.7 请求限流 #

javascript
const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
    windowMs: 15 * 60 * 1000,
    max: 100,
    message: { error: '请求过于频繁,请稍后再试' }
});

app.use('/api', limiter);

六、中间件执行顺序 #

6.1 顺序很重要 #

javascript
app.use(express.json());

app.use((req, res, next) => {
    console.log(req.body);
    next();
});

app.post('/data', (req, res) => {
    res.json(req.body);
});

如果 express.json() 放在后面,req.body 会是 undefined

6.2 错误处理中间件位置 #

javascript
app.get('/', (req, res) => {
    res.send('Hello');
});

app.use((err, req, res, next) => {
    res.status(500).json({ error: err.message });
});

错误处理中间件必须放在所有路由之后。

6.3 404处理 #

javascript
app.get('/', (req, res) => {
    res.send('Hello');
});

app.use((req, res) => {
    res.status(404).json({ error: '路由未找到' });
});

404中间件放在所有路由之后,错误处理中间件之前。

七、中间件最佳实践 #

7.1 模块化中间件 #

middlewares/logger.js:

javascript
const logger = (req, res, next) => {
    console.log(`${req.method} ${req.url} - ${new Date().toISOString()}`);
    next();
};

module.exports = logger;

使用:

javascript
const logger = require('./middlewares/logger');
app.use(logger);

7.2 中间件工厂 #

javascript
const createLogger = (options = {}) => {
    return (req, res, next) => {
        if (options.logBody) {
            console.log('Body:', req.body);
        }
        console.log(`${req.method} ${req.url}`);
        next();
    };
};

app.use(createLogger({ logBody: true }));

7.3 异步中间件处理 #

javascript
const asyncHandler = fn => (req, res, next) => {
    Promise.resolve(fn(req, res, next)).catch(next);
};

app.get('/users', asyncHandler(async (req, res) => {
    const users = await User.find();
    res.json(users);
}));

7.4 条件中间件 #

javascript
const conditionalMiddleware = (condition, middleware) => {
    return (req, res, next) => {
        if (condition(req)) {
            return middleware(req, res, next);
        }
        next();
    };
};

app.use(conditionalMiddleware(
    req => req.path.startsWith('/api'),
    (req, res, next) => {
        console.log('API请求');
        next();
    }
));

八、中间件调试 #

8.1 调试中间件链 #

javascript
app.use((req, res, next) => {
    console.log('1. 第一个中间件');
    next();
});

app.use((req, res, next) => {
    console.log('2. 第二个中间件');
    next();
});

app.get('/', (req, res, next) => {
    console.log('3. 路由处理');
    next();
});

app.use((req, res) => {
    console.log('4. 最后的处理');
    res.send('Done');
});

8.2 使用debug模块 #

bash
npm install debug
javascript
const debug = require('debug')('app:middleware');

app.use((req, res, next) => {
    debug('请求: %s %s', req.method, req.url);
    next();
});

运行:

bash
DEBUG=app:* node app.js

九、总结 #

中间件核心概念:

概念 说明
定义 访问req、res、next的函数
next() 调用下一个中间件
执行顺序 按定义顺序执行
类型 应用级、路由级、错误处理、内置、第三方
挂载 app.use()、router.use()

下一步,让我们深入学习应用级中间件!

最后更新:2026-03-28