【问题标题】:Adding a catch block to a promise returns pending instead of rejected将 catch 块添加到 Promise 会返回待处理而不是被拒绝
【发布时间】:2021-01-15 15:33:12
【问题描述】:

我有一个正在运行的 apollo graphql express 服务器。唯一的问题是 express 抱怨我在用来验证 jwt 令牌的 Promise 上没有 catch 块:

(node:96074) UnhandledPromiseRejectionWarning:未处理的承诺拒绝。此错误源于在没有 catch 块的情况下抛出异步函数内部,或拒绝未使用 .catch() 处理的承诺

我可以在 promise 中添加一个 catch 块,但是当令牌失效时它会返回 pending 而不是 rejected。这会导致身份验证流程中断,因为我的 graphql 解析器依赖该拒绝来阻止对数据库的访问。

这就是我用于身份验证的 auth0 建议设置它的方式。他们只是没有提到 UnhandledPromiseRejectionWarning。

代码如下所示:

//server def
const server = new ApolloServer({ 
    typeDefs, 
    resolvers, 
    context: ({ req }) => {
        if (req.headers.authorization) {
            const token = req.headers.authorization.split(' ')[1];

            //THE PROMISE IN QUESTION
            const authUserObj = new Promise((resolve, reject) => {
                jwt.verify(token, getKey, options, (err, decoded) => {
                    if (err) {
                        reject(err);
                    }
                    if (decoded) {
                        resolve(decoded); 
                    }
                });
            });

            return {
                authUserObj
            };
        }
    },
    introspection: true,
    playground: true
});

//a graphql resolver that gets the rejection via authUserObj and catches the error 
addUser: async (parent, args, {authUserObj}) => {
            try {
                const AuthUser = await authUserObj;
                const response = await User.create(args);
                return response;
            } catch(err) {
                throw new AuthenticationError('You must be logged in to do this');
            }
        }

一切正常...除了我希望克服的那个烦人的节点错误!所以我在 promise 中添加了一个 catch 块:

 const authUserObj = new Promise((resolve, reject) => {
                jwt.verify(token, getKey, options, (err, decoded) => {
                    if (err) {
                        console.log("-------rejected-------", err.message)
                        reject(err);
                    }
                    if (decoded) {
                        console.log("-------decoded-------")
                        resolve(decoded); 
                    }
                });
            }).catch( err => { return err.message});

现在 authUserObj 不是返回被拒绝,而是处于挂起状态,任何人都可以添加用户,这违背了 auth 的目的。

如果有人知道如何在仍然拒绝该错误的同时捕获该错误,我会全力以赴。谢谢。

【问题讨论】:

    标签: javascript node.js error-handling promise graphql


    【解决方案1】:

    问题不在于未处理的 Promise 拒绝,而更多地在于未处理的 Promise。您尝试在 context 对象中放入一个承诺,然后在 addUser 解析器中放入 await 承诺。在其他解析器中,promise 可能根本不会被使用,并且当 jwt 验证失败时,拒绝将不会被处理。 (另外,如果解析器是异步执行的,promise 可能会在它们处理它之前被拒绝。

    相反,whole context initialisation 应该异步完成,返回带有用户详细信息的上下文对象的承诺。这意味着请求甚至在开始执行查询之前就会失败:

    const server = new ApolloServer({ 
        typeDefs, 
        resolvers, 
        context: ({ req }) => {
            if (req.headers.authorization) {
                const token = req.headers.authorization.split(' ')[1];
                return new Promise((resolve, reject) => {
                    jwt.verify(token, getKey, options, (err, decoded) => {
                        if (err) reject(err);
                        else resolve(decoded);
                    });
                }).then(authUser => {
                    if (authUser) return { authUser };
                    // else return {};
                });
                // .catch(err => { … }) - you may chose to ignore verification failure,
                // and still return a context object (without an `authUser`)
            }
            // else return {}; - when not sending the header, no token will be checked at all
        },
        introspection: true,
        playground: true
    });
    
    // a graphql resolver that checks for the authUserObj
    addUser: async (parent, args, {authUserObj}) => {
        if (!authUserObj) { // you might also want to check specific claims of the jwt
            throw new AuthenticationError('You must be logged in to do this');
        }
        const response = await User.create(args);
        return response;
    }
    

    【讨论】:

    • 好点,谢谢。我会这样做的。
    • @j1mmy 在这种情况下,您可能想接受我的回答。很高兴能提供帮助!
    【解决方案2】:

    就像try/catch 一样,如果您只是从.catch() 处理程序返回一个正常值(或不返回任何内容),.catch() 会将承诺链从拒绝更改为已解决。当您返回“正常”值时,拒绝被视为“已处理”,并且承诺链将使用该新值解决。这就是您处理错误并继续正常处理的方式。

    要保持 Promise 链被拒绝,您必须 throw 或返回被拒绝的 Promise。这将使承诺链保持被拒绝。

    所以,如果你想让authUserObj 保持被拒绝的承诺,那么改变这个:

    }).catch( err => { return err.message});
    

    到这里:

    }).catch( err => { return Promise.reject(err.message)});
    

    或类似的东西,要么引发错误,要么返回被拒绝的承诺。

    【讨论】:

    • 谢谢你说得非常清楚,并且有效。非常感谢。
    • 这不是用于 apollo 服务器的正确解决方案。我会尽快写一个答案
    • 另外,现在我再次阅读它,我不确定重新抛出错误将如何有助于避免未处理的拒绝......所以这根本不是一个解决方案。
    • @j1mmy - 这解释了你的问题的标题。提醒您,该标题是 将 catch 块添加到 Promise 会返回待处理而不是被拒绝
    猜你喜欢
    • 2019-02-28
    • 2019-05-26
    • 2021-02-20
    • 1970-01-01
    • 1970-01-01
    • 2021-06-06
    • 2021-02-04
    • 2019-04-15
    • 2018-08-17
    相关资源
    最近更新 更多