V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
qfdk
V2EX  ›  问与答

请教各位老哥,有没有优雅的方式,在 express 中 如何先读取数据库,再启动后面的 中间件 ?

  •  
  •   qfdk · 2021-09-08 01:36:05 +08:00 · 664 次点击
    这是一个创建于 962 天前的主题,其中的信息可能已经有所发展或是发生改变。

    用 express 正在搞一个 oidc 服务器,认证服务器。 现在碰到点儿困难,官方文档的 clients 是写死在配置文件中的,只接受一个数组。

    https://github.com/panva/node-oidc-provider 这个是使用的库

    express 需要调用这个中间件。姑且叫做 oidc

    我想要插数据库 拿到所有的 clients 信息,然后再来配置这个中间件中的 clients

    因为查询数据库是异步的,也比较耗时,是 async 的程序,这样 app.js 中 都需要包裹在

    (async ()=>{
    const client = await queryDb();
    const oidc = new OIDC({client});
     app.use(abc);
     app.use(oidc.callback());
     // module.exports = app; // 无法在 bin/www 启动
     app.listen()
    })()
    

    没有办法完成 export 到另外的 bin/www 文件启动。只能用 app.listen 来启动服务器。 我在想要是不改变文件夹结构的话,不知道要有啥法子 ?

    queryDb().then(clients=>{
     const oidc = new OIDC({client});
     app.use(abc);
     app.use(oidc.callback());
    });
    module.exports = app;
    

    这样感觉理论上应该没问题。 但是问题又来了 app 。use 是中间件 我想把 OIDC 的逻辑仍在另一个 config 文件夹里面 只返回 oidc 像这样

    const clients= await queryDb(); // 这里需要放在 async 里面 这种方案理论上不行。因为要包裹在 async 里面,于是想到写个 init 
    const oidc = new OIDC({client});
    module.exports={oidc}
    
    
    const {oidc} = require('./config');
    app.use(abc);
    app.use(oidc.callback());
    module.exports = app;
    

    想了三种方案,还是看看各位吴彦祖有没有好法子。先谢过了 看到个 类似的问题 https://stackoverflow.com/questions/49991740/can-i-export-the-result-of-an-asynchronous-function-with-module-export

    3 条回复    2021-09-08 15:12:30 +08:00
    TomVista
        1
    TomVista  
       2021-09-08 09:14:20 +08:00 via Android
    自己搜一下 node 顶层 await,

    我本来给你找好了文档,但是没发出去,而且站长封了我的 ip,神仙
    Cbdy
        2
    Cbdy  
       2021-09-08 09:27:09 +08:00 via Android
    1. top-level await
    2. 导出工厂函数
    qfdk
        3
    qfdk  
    OP
       2021-09-08 15:12:30 +08:00
    @TomVista #1 谢谢老哥!
    @Cbdy 感谢思路 !

    虽然问题还没有解决。

    现在的问题,我要是 commonJS 的话 顶层 await 应该没法用吧,

    谷歌了一下,说是 es2021 的新特性. 需要把 js 改成 mjs 。 不知道还有没有别的好法子。

    我现在想的 一个是用事件。

    把所有 app 用 event 包起来。如果 queryDb 事件完成,则执行里面的代码。

    ```javascript
    myEmitter.on('ok', (data) => {
    console.log('data');
    app.get('/test', (req, res) => {res.send(data);}); // 可行
    });
    ```

    根据 @TomVista @Cbdy 顶层 await 的思路,需要改成 es6 的语法用 import 。
    ```javascript
    // test.mjs ,因为需要支持语法 要用 mjs
    const queryData = await import('./test-config.mjs');
    class ABC {
    constructor(name, data) {
    this.name = name;
    this.data = data;
    }
    }

    const abc = new ABC('a', queryData);
    console.log(abc); // 有数据
    export default abc;
    ```

    ```javascript
    // app.js
    // express 自动生成代码
    const app = express();
    const abc = import('./config/test.mjs'); // 这里也要加入 await 么 ?如果加入 await 的话,这个 app.js 也需要改名了=> 上层 bin/www 内部引用也要改名
    console.log(abc); // 这里还是 pending promise,没有数据
    // view engine setup
    app.set('views', path.join(__dirname, 'views')
    ...
    ```
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1057 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 23:05 · PVG 07:05 · LAX 16:05 · JFK 19:05
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.