了解 Express.js 中的中间件及其内部工作原理

在 Express.js 中,中间件是可以访问请求 (req)、响应 (res) 和第三个参数 next 的特殊函数。与常规路由处理程序不同,中间件通过在主要业务逻辑之前执行外部逻辑,在控制应用程序流程方面发挥着关键作用。

中间件如何工作?

当 HTTP 请求到达 Express.js 服务器时,它会流经一系列中间件函数。每个中间件都可以:

  • 修改请求对象(例如,附加数据、验证令牌)。
  • 修改响应对象(例如,提前发送响应)。
  • 使用 next() 函数将控制权传递给堆栈中的下一个中间件。
  • 如果中间件不调用 next(),则请求-响应周期将终止,并且不会执行任何进一步的逻辑(包括路由处理程序)。

    为什么我们使用中间件?

    中间件非常适合我们需要在处理请求之前添加可重用逻辑的场景。例如:

  • 身份验证:检查用户是否已登录(例如,验证 - JWT 令牌)。
  • 授权:确保用户具有执行某些操作的必要权限(例如,管理员可以删除内容)。
  • 请求验证:验证是否提供了所有必需的输入。
  • 日志记录和监控:记录传入请求的详细信息以供分析或调试。
  • 错误处理:全局捕获错误以发送有意义的响应。
  • 定义和使用中间件

    中间件函数如下所示:

    app.use((req, res, next) => {
        // Logic here
        next(); // Pass control to the next middleware or route handler
    });
  • req(请求):包含有关传入 HTTP 请求的信息(例如,标头、正文、参数)。
  • res(响应):用于将数据发送回客户端。next():将控制权传递给下一个中间件的函数。
  • 中间件流程:执行顺序

    中间件顺序很重要! Express 按照定义的顺序依次执行中间件。

    如果中间件在路由之后定义,则不会影响该路由。这就是为什么必须在 app.js 中的路由之前声明中间件的原因。

    例子:

    // Middleware to check if the user has admin privileges
    app.use((req, res, next) => {
        console.log("Checking for admin role...");
    
        // Simulating a user object attached earlier in the pipeline
        if (req.user && req.user.role === "admin") {
            console.log("Access granted");
            next(); // Move to the next middleware or route handler
        } else {
            console.log("Access denied");
            res.status(403).send("You do not have access to this resource.");
        }
    });
    
    // Routes
    app.get("/admin/dashboard", (req, res) => {
        res.send("Welcome to the admin dashboard!");
    });
    
    app.get("/public", (req, res) => {
        res.send("This is a public page.");
    });

    内部执行流程

    以下是具体发生的情况:

  • 传入请求:请求到达服务器。
  • 中间件执行:中间件检查 req.user.role。如果角色为“admin”,则调用 next() 将控制权传递给下一个中间件或路由。如果不是,则中间件通过发送 403 Forbidden 响应来终止请求。
  • 路由处理程序:如果调用 next(),则执行相关的路由处理程序(例如 /admin/dashboard)。
  • 示例流程:

  • 具有角色“admin”的用户请求 /admin/dashboard。中间件记录“访问已授予”并调用 next()。路由处理程序发送“欢迎使用管理仪表板!”。
  • 具有角色“user”的用户请求 /admin/dashboard。中间件记录“拒绝访问”并发送“您无权访问此资源”。
  • 关键要点

  • 中间件就像守门人一样——它们决定主路线逻辑运行之前发生什么。
  • 使用 next() 确保流程继续到下一个中​​间件或路由。
  • 始终在路由之前定义关键中间件以确保它们适用。
  • 如果您不调用 next() 或发送响应,请求将挂起(超时)。