2)手写中间件(Express)
总结
- 中间件的格式:
(req, res, next)
或(err, req, res, next)
next
是实现链式调用的关键。- Express 内部通过一个 迭代器机制 逐个执行中间件,
next
通过递归调用实现了这一过程。
- 使用方式:
- 通过
app.use()
全局挂载。 - 通过特定路由挂载。
- 通过
- 中间件执行顺序:从上到下依次执行。
- 错误处理中间件通过
next(err)
实现跳过普通中间件,直接处理错误逻辑。
手写 Express 中间件的步骤
定义一个中间件函数:
- 中间件是一个接受
(req, res, next)
参数的函数。 req
和res
是 Express 提供的请求和响应对象,next
是调用链中下一个中间件的控制函数。
- 中间件是一个接受
在应用中使用中间件:
- 通过
app.use()
全局挂载。 - 通过特定路由挂载。
- 通过
实现具体功能:
- 中间件可用于日志记录、身份验证、请求解析等任务。
完整代码示例
1. 创建一个简单的日志中间件
javascript
// Import the Express library
import express from "express";
const app = express();
// Custom middleware for logging requests
const requestLogger = (req, res, next) => {
const { method, url } = req;
const timestamp = new Date().toISOString();
console.log(`[${timestamp}] ${method} ${url}`);
next(); // Proceed to the next middleware or route handler
};
// Use the middleware globally
app.use(requestLogger);
// Route example
app.get("/", (req, res) => {
res.send("Hello, world!");
});
// Another route
app.get("/about", (req, res) => {
res.send("About page");
});
// Start the server
const PORT = 3000;
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
2. 中间件的扩展示例
身份验证中间件
javascript
// Middleware for authentication
const authenticate = (req, res, next) => {
const token = req.headers["authorization"];
if (!token) {
return res.status(401).json({ message: "Unauthorized" });
}
// Simulate token verification (replace with real logic)
if (token === "valid-token") {
next(); // Proceed to the next middleware or route
} else {
res.status(403).json({ message: "Forbidden" });
}
};
// Apply to a specific route
app.get("/protected", authenticate, (req, res) => {
res.send("This is a protected route.");
});
3. 错误处理中间件
javascript
// Error handling middleware
const errorHandler = (err, req, res, next) => {
console.error(err.stack); // Log the error stack
res
.status(500)
.json({ message: "Something went wrong!", error: err.message });
};
// Example of a route with an error
app.get("/error", (req, res) => {
throw new Error("This is a simulated error");
});
// Use the error handling middleware (should be the last middleware)
app.use(errorHandler);
中间件的核心点
next()
的作用:next()
是调用链的核心。如果不调用,后续中间件和路由将无法执行。
错误处理中间件的特殊性:
- 必须定义
err
参数,形式为(err, req, res, next)
。
- 必须定义
挂载顺序:
- 中间件的执行顺序与注册顺序一致,顺序错误可能导致意外行为。
运行结果
启动服务器:
node app.js
。请求
/
、/about
等路由:- 日志中间件会打印类似:
[2024-12-25T08:00:00.000Z] GET /
- 日志中间件会打印类似:
请求
/protected
路由:- 如果
Authorization
头不为valid-token
,返回:json{ "message": "Unauthorized" }
- 如果
请求
/error
:- 返回错误信息:json
{ "message": "Something went wrong!", "error": "This is a simulated error" }
- 返回错误信息:
Express 迭代器核心代码
以下是一个简化的 Express 核心实现。
js
class Express {
constructor() {
// 默认的中间件数组
this.middlewares = [];
}
use(middleware) {
// 插入中间件到数组中
this.middlewares.push(middleware);
}
handleRequest(req, res) {
// 默认的中间件索引
let index = 0;
const next = (err) => {
// 如果超过了中间件数组长度,结束
if (index >= this.middlewares.length) return;
// 当前中间件
const middleware = this.middlewares[index++];
if (err) {
// 如果是错误处理中间件,调用它
if (middleware.length === 4) {
return middleware(err, req, res, next);
} else {
// 跳过普通中间件
return next(err);
}
}
if (middleware.length < 4) {
return middleware(req, res, next); // 调用普通中间件
}
next(); // 跳过错误处理中间件
};
next(); // 开始执行第一个中间件
}
}
// 使用
const app = new Express();
app.use((req, res, next) => {
console.log("Middleware 1");
next();
});
app.use((req, res, next) => {
console.log("Middleware 2");
res.end("Response from Middleware 2");
});
// 模拟请求
app.handleRequest({}, { end: console.log });