如何从 Express Server 中的不同文件夹加载 API 个端点?

How to load API Endpoints from different folders in Express Server?

我目前使用此脚本加载我的 API 端点:

readdirSync('./routes/api').map((r) =>
  app.use(
    `/api/v1/${r.split('.')[0]}`,
    require(`./routes/api/${r.split('.')[0]}`)
  )
);

它几乎读取了 routes/api 文件夹中的所有内容,我在 POSTMAN 中访问端点的方式如下:

{{URL}}/api/v1/posts/123
{{URL}}/api/v1/videos/123
{{URL}}/api/v1/users/123
and so on

现在我想做的是创建将用作工具的端点,比方说,视频转换器端点?

此外,我想将此端点放在路由文件夹内的 extras 文件夹中,如下图所示:

调用 extras 文件夹内的端点的方法如下所示:

{{URL}}/api/v1/extras/posts/123
{{URL}}/api/v1/extras/videos/123
{{URL}}/api/v1/extras/users/123
and so on

然后我决定 运行 我的服务器用第二个新脚本看看它是否可以 运行...

readdirSync('./routes/api/extras').map((r) =>
  app.use(
    `/api/v1/extras/${r.split('.')[0]}`,
    require(`./routes/api/extras/${r.split('.')[0]}`)
  )
);

好吧,它没有。我收到一个错误消息:

[0] Error: Cannot find module './routes/api/extras'
[0] Require stack:
[0] - C:\xampp\htdocs\befree\server.js
[0]     at Function.Module._resolveFilename (internal/modules/cjs/loader.js:902:15)
[0]     at Function.Module._load (internal/modules/cjs/loader.js:746:27)
[0]     at Module.require (internal/modules/cjs/loader.js:974:19)
[0]     at require (internal/modules/cjs/helpers.js:93:18)
[0]     at C:\xampp\htdocs\befree\server.js:165:5
[0]     at Array.map (<anonymous>)
[0]     at Object.<anonymous> (C:\xampp\htdocs\befree\server.js:162:29)
[0]     at Module._compile (internal/modules/cjs/loader.js:1085:14)
[0]     at Object.Module._extensions..js (internal/modules/cjs/loader.js:1114:10)
[0]     at Module.load (internal/modules/cjs/loader.js:950:32) {
[0]   code: 'MODULE_NOT_FOUND',
[0]   requireStack: [ 'C:\xampp\htdocs\befree\server.js' ]
[0] }
[0] Error Cannot find module './routes/api/extras'
[0] Require stack:
[0] - C:\xampp\htdocs\befree\server.js
[0] [nodemon] app crashed - waiting for file changes before starting...

有谁知道有没有办法解决这个问题?

fs.readdir 将读取提供目录中的所有文件,即 目录中的文件。这意味着当执行 readdirSync('./routes/api') 时,目录名称 extras 也将在 .map 回调中传递给 require 并且由于它是一个目录,因此将完成以下操作(来自 docs):

[...]if the filename passed to require is actually a directory, it will first look for package.json in the directory and load the file referenced in the main property. Otherwise, it will look for an index.js.

在你的情况下,以上两者似乎都没有找到,因此你得到错误 Cannot find module './routes/api/extras'

一种可能的解决方案是过滤掉目录,只处理文件,例如:

readdirSync('./routes/api')
.filter(r => fs.lstatSync('./routes/api/'+ r).isFile())
.map((r) =>
  app.use(
    `/api/v1/${r.split('.')[0]}`,
    require(`./routes/api/${r.split('.')[0]}`)
  )
);

我无法创建我想要的路线

/api/v1/extras/youtube
and so on

但是,我能够通过创建一个遍历每个文件夹的函数来消除之前显示的错误。

// Define routes
const dir = './routes/api';

function getFiles(dir) {
  return fs.readdirSync(dir).flatMap((item) => {
    // const file = item.split('.')[0];
    const routePath = `${dir}/${item}`;
    if (fs.statSync(routePath).isDirectory()) {
      return getFiles(routePath);
    }
    // return { singlefile: file, directories: routePath };
    return { item, routePath };
  });
}

const routes = getFiles(dir);

routes.map((result) => {
  app.use(
    `/api/v1/${result.item.split('.')[0]}`,
    require(`.${result.routePath.split('.')[1]}`)
  );
});

我的 youtube 路由位于 routes>api>extras>youtube.js 但我仍然需要这样称呼它:

/api/v1/youtube

我想说的是够好!

2021 年 10 月 27 日更新

我最终修改了我的循环,而不是使用两个返回值,itemroutePath,我选择只使用 routePath 并修改更完整的字符串满足我的需要。

routes.map((result) => {
  const firstRoute = result.routePath
    .split(`./routes`)[1]
    .replace(`/api/`, `/api/v1/`)
    .split('.')[0];
  app.use(`${firstRoute}`, require(`.${result.routePath.split('.')[1]}`));
});

这个小技巧实际上让我可以调用我的端点很多层。