NodeJS ExpressJS:在运行时动态添加路由

uxhixvfz  于 2023-04-20  发布在  Node.js
关注(0)|答案(2)|浏览(291)

我希望能够在运行时添加新的路由,而无需使用NodeJS和ExpressJS重新启动服务器。我在这篇文章中提出了类似的方法:https://alexanderzeitler.com/articles/expressjs-dynamic-runtime-routing/
从技术上讲,我可以在运行时添加新的文件和逻辑,但问题是,当没有匹配的API路由时,我将发送404 JSON响应(正如它应该的那样)。
我想我遇到的问题是我的动态创建的路由永远不会到达,因为静态路由的优先级高于动态创建的路由。这意味着创建的路由将在错误处理后挂载,因此永远不会到达。我在app.js中的代码

...

// Routes
app.use('/api/products', productRoutes);
app.use('/api/users', userRoutes);

...

/* This is where the dynamically created routes should be mounted */

// Error handling
app.use((req, res, next) => {
    const err = new Error('Not found');
    err.status = 404;
    next(err);
});

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

/* This is where the dynamic routes are mounted */

module.exports = app;

当我注解掉错误处理时,我可以访问我在运行时创建的路由,而使用错误处理时,我只能访问服务器重启后动态创建的路由,这是我想要避免的。
这个问题不能用查询参数来解决,因为动态添加的路由在逻辑、模型属性、http方法/动词和API端点上不同。
GET/POST /API/{endpoint}
GET/POST /API/foo/{endpoint}
GET/PUT/DELETE /API/foo/bar/{endpoint}/:id
我认为我基本上需要:
1)找到一种方法,在错误处理之前挂载动态创建的路由-我目前停留在或
2)修改路由栈-我读到的是不切实际的,缓慢的,不好的做法和容易出错的
3)找到替代解决方案
我希望有人能帮助我。
先谢谢你了

编辑

下面是创建新路由的代码。

const Database = require('../models/database');
const controller = require('./template/controller');
const creation = require('../Creation');

...

exports.createOne = (req, res, next) => {
  if (!creation.findFileInDirectory(`./backend/api/models/${req.body.name.singular}.js`) ||
      !creation.findFileInDirectory(`./backend/api/controllers/${req.body.name.singular}.js`) ||
      !creation.findFileInDirectory(`./backend/api/routes/${req.body.name.singular}.js`)) {
    controller.createOne(req, res, next, Database, {
      modelName: 'database',
    }, () => {
      //creation.createEndpoint(req.body.name, req.body.data, req.body.auth);
      creation.createEndpoint(req.body.name, req.body, req.body.auth);
    });
  } else {
    res.status(422).json({message: 'Endpoint exists already'});
  }
}

...

代码片段中的控制器只是一个模块化的控制器文件,它处理我对不同模型的所有端点的所有CRUD操作。每个路由都被拆分为模型,控制器和路由,以分离和更好地维护它们的逻辑。
在POST方法中,我首先检查要创建的端点是否已经存在。如果存在,我用422响应该端点已经存在。如果不存在,我在数据库端点中创建一个条目mith我的模块化控制器,并为应该创建的端点创建一个模型,控制器和路由。
创建逻辑如下:

const createEndpoint = (name, data, auth) => {
    createFile(`./backend/api/models/${name.singular}.js`, model.createModel(capitalize(name.singular), data), () => {
      createFile(`./backend/api/controllers/${name.singular}.js`, controller.createController({singular: capitalize(name.singular), plural: name.plural}, data.data), () => {
        createFile(`./backend/api/routes/${name.singular}.js`, route.createRoute({singular: capitalize(name.singular), plural: name.plural}, auth), () => {
          const app = require('../../app');
          mountEndpoints(name.singular, app);
        });
      });
    });
};

在这里,我基本上将数据从POST方法传递到异步创建的模型,控制器和路由文件。当所有文件创建完毕时,我将端点路由挂载到应用程序。挂载路由的逻辑是:

const mountEndpoints = (path, app) => {
  const module = require(`../routes/${path}`);
  app.use(`/api/${module.plural ? `${module.plural}` : `${path}s`}`, module);
}

创建的路由可能如下所示:

const express   = require('express');
const router    = express.Router();
const checkAuth = require('../middleware/check-auth');

const ProductController = require('../controllers/product');

router.route('/')
    .get(ProductController.getAll)
    .post(checkAuth, ProductController.createOne);

router.route('/:id')
    .get(ProductController.getOne)
    .patch(checkAuth, ProductController.patchOne)
    .delete(checkAuth, ProductController.deleteOne);

module.exports = router;
module.exports.plural = 'products';

checkAuth包括一些用于授权/身份验证的逻辑。
这段代码基本上完成了我想要它做的事情,除了我不知道如何在错误处理之前处理路由的定位。

ny6fqffe

ny6fqffe1#

快速路由将按创建顺序处理。
要在app定义后的特定位置添加路由,您可以创建一个占位符路由器并将路由附加到该占位符路由器,而无需修改app本身。
Express不支持在定义路由后删除路由,但您可以替换整个路由器。
创建一个express路由器示例(如果需要,甚至可以是另一个app)来挂载动态端点。每当你想更改路由时,都要重新定义路由器(除了添加到路由器堆栈的末尾,express支持)。

// Routes
app.use('/api/products', productRoutes);
app.use('/api/users', userRoutes);

let dynamicApiRouter = null

export function setupDynamicRouter(route_configs) {
  dynamicApiRouter = new express.Router()
  // Add routes to dynamicApiRouter from `route_configs`
  for (const config of route_configs) {
    dynamicApiRouter[config.method](config.path, config.handler)
  }
}

app.use('/api', (req, res, next) => dynamicApiRouter(req, res, next))

// Error handling
app.use((req, res, next) => {
    const err = new Error('Not found');
    err.status = 404;
    next(err);
});

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

setupDynamicRouter()可以在任何时候使用一个或一系列路由和处理程序来调用:

const route_config = [
  {
    method: 'get',
    path: '/sales',
    handler: (req, res, next) => res.json({ ok: true }),
  },
  {
    method: 'post',
    path: '/sales',
    handler: (req, res, next) => res.json({ post: true }),
  },
])
setupDynamicRouter(route_config)

对于问题示例“routes”设置,/api路径前缀现在位于父app中的路由器挂载上,因此可以从每个router.use中删除

const mountEndpoints = (path, router) => {
  const module = require(`../routes/${path}`);
  router.use(`/${module.plural ? `${module.plural}` : `${path}s`}`, module);
}
iklwldmw

iklwldmw2#

只是添加到上面的答案,这是正确的,你基本上需要一个express应用程序或Router示例来附加你的路由。
在我的例子中,我有一个异步初始化的遗留Node.js应用程序,这意味着底层express中间件在内部初始化数据库连接后返回。
这里的myApp是一个遗留应用程序,它有一个init方法,该方法带有一个回调函数,在一段时间后返回express中间件instance

var express = require('express')
var myApp = require('my-app')

module.exports = (config) => {
  // create an app handle
  var handle = express()

  myApp.init(config, (err, instance) => {
    if (err) {}
    // mount lazily
    handle.use(instance)
  })

  // return immediately
  return handle
}

我把这个 Package 器模块命名为lazy-load.js,然后像这样使用它:

var express = require('express')
var lazyLoad = require('lazy-load')

express()
  .use('/a', lazyLoad(require('./config-a.json')))
  .use('/b', lazyLoad(require('./config-b.json')))
  .listen(3000)

我想为同一个应用程序提供多个示例,但为不同的用户提供不同的配置。
至于最初的问题,只要您保持对handle中间件示例的引用,最初连接到服务器,您就可以在运行时向它添加新路由。

相关问题