发布于 2026-01-06 3 阅读
0

'Express Middleware' is just a fancy way of chaining a bunch of functions. Explained in 3 mins 😎 Middleware == Function with next() Example 1: Require user authentication for certain routes. Example 2: Require user authentication for all routes. Example 3: Parse JSON request body into object. Example 4: Configurable Middlewares

Express Middleware 只是将多个函数串联起来的一种巧妙方式。三分钟就能解释清楚 😎

中间件 == 带有函数next()

示例 1:对某些路由要求用户身份验证。

示例 2:要求所有路由都进行用户身份验证。

示例 3:将 JSON 请求体解析为对象。

示例 4:可配置中间件

让我们从创建世界上最简单的 Express 服务器开始:

import express from "express";

const app = express();

app.get('/hello', (req, res) => {
  res.send('world'));
}
Enter fullscreen mode Exit fullscreen mode

它创建了一条/hello响应身体的路线world

现在让我们稍微修改一下最后一行,添加一个名为“:”的第三个参数next

app.get('/hello', (req, res, next) => {
  res.send('world'));
  next();
}
Enter fullscreen mode Exit fullscreen mode

恭喜!🎉🎉🎉 您刚刚创建了一个 Express 中间件。就这么简单!

中间件 == 带有函数next()

中间件就是一个带有三个参数的函数(req, res, next)next它允许你链式调用多个函数。我们来看另一个例子:


// returns true if server time is in AM
function isMorning() {...}

app.get("/hello",
  // middleware #1
  (req, res, next) => {
    if (isMorning()) {
      res.send("morning");
    } else {
      next();
    }
  },
  // middleware #2: called when isMorning() === false
  (req, res, next) => {
    res.send("afternoon");
  }
);
Enter fullscreen mode Exit fullscreen mode

这里我们链接了两个中间件函数来处理/hello路由。我们使用 `if` 语句next()将控制权从第一个中间件传递给第二个中间件。

在实际应用中,中间件对于在不同路由之间共享通用代码非常有用。

示例 1:对某些路由要求用户身份验证。

这里我们创建了一个中间件,它仅next()在用户通过身份验证后才会调用。该中间件由两个路由共享。请注意,当用户未通过身份验证时,我们不会调用该中间件next(),这将终止整个调用链

function RequireUserAuthentication(req, res, next) {
  if (req.user == null) {
    res.status("401").send("User is unauthenticated.");
  } else {
    next();
  }
}

app.get("/me/name", RequireUserAuthentication, (req, res, next) => {
  res.send(req.user.name);
});

app.get("/me/avatar", RequireUserAuthentication, (req, res, next) => {
  res.send(req.user.avatar);
});
Enter fullscreen mode Exit fullscreen mode

如果我们想在所有路由上而不是特定路由上使用 RequireUserAuthentication,该怎么办?

示例 2:要求所有路由都进行用户身份验证。

我们可以使用中间件app.use(RequireUserAuthentication),将其RequireUserAuthentication“注入”到所有路由中。

需要注意的是,中间件是有顺序的。在下面的代码中,任何之前定义的路由app.use(RequireUserAuthentication)都不会受到影响。

// This route is not affected by RequireUserAuthentication
app.get("/home", (req, res, next) => res.send(...));

// Require user auth for all routes defined after this line.
app.use(RequireUserAuthentication);

// Routes below are user sign-in required
app.get("/me/name", (req, res, next) => {
  res.send(req.user.name);
});

app.get("/me/avatar", (req, res, next) => {
  res.send(req.user.avatar);
});
Enter fullscreen mode Exit fullscreen mode

示例 3:将 JSON 请求体解析为对象。

有时将请求体自动转换为 JSON 对象很有用,这样我们就不必为每个路由编写相同的逻辑了:

// Converts request body into req.body as a javascript object
function JSONParser(req, res, next) {
  if (req.headers['content-type'].startsWith('application/json')) {
    const rawBody = readStreamIntoString(req);
    req.body = JSON.parse(rawBody);
  }
  next();
}

// Apply JSONParser middleware to all routes defined after this line
app.use(JSONParser);

// Reads post name and content from req.body
app.get("/new/post", (req, res, next) => {
  const postTitle = req.body.title;
  const postContent = req.body.content;
  ...
});

// Reads username from req.body
app.get("/new/user", (req, res, next) => {
  const userName = req.body.username;
  ...
});
Enter fullscreen mode Exit fullscreen mode

这里我们创建了一个JSONParser中间件,它将 JSON 请求体解析成一个对象,并将该对象设置为。之后,该对象会从路由以及之后定义的任何其他路由req.body中读取。/new/post

示例 4:可配置中间件

让我们来点儿花样,创建一个“工厂”函数来返回中间件函数。一个函数的函数🤖

function BodyParser(options) {
  if (options.type === "JSON") {
    return (req, res, next) => {
      if (req.headers["content-type"].startsWith("application/json")) {
        const rawBody = readStreamIntoString(req);
        req.body = JSON.parse(rawBody);
      }
      next();
    };
  } else if (options.type === "URL_ENCODED") {
    return (req, res, next) => {
      if (
        req.headers["content-type"].startsWith(
          "application/x-www-form-urlencoded"
        )
      ) {
        const rawBody = readStreamIntoString(req);
        req.body = new URLSearchParams(rawBody);
      }
      next();
    };
  }
}

app.use(BodyParser({ type: "JSON" }));
app.use(BodyParser({ type: "URL_ENCODED" }));
Enter fullscreen mode Exit fullscreen mode

在上面的代码中,我们允许传入一个options参数来控制要返回哪个中间件函数。

事实上,bodyParser正是通过解析请求体来实现这一点的(当然,它的代码更加复杂)。

你们在生产环境中使用了哪些中间件函数?请在下方留言分享你们最喜欢的函数❤️❤️❤️!

快来访问getd.io,并留下您对未来功能需求的反馈意见❤️❤️❤️!

文章来源:https://dev.to/getd/express-middleware-is-just-a-fancy-way-of-chaining-a-bunch-of-functions-explained-in-3-mins-43jf