将 catch 块添加到 Promise 会返回待处理而不是被拒绝
Posted
技术标签:
【中文标题】将 catch 块添加到 Promise 会返回待处理而不是被拒绝【英文标题】:Adding a catch block to a promise returns pending instead of rejected 【发布时间】: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 的目的。
如果有人知道如何在仍然拒绝该错误的同时捕获该错误,我会全力以赴。谢谢。
【问题讨论】:
【参考方案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 会返回待处理而不是被拒绝,以上是关于将 catch 块添加到 Promise 会返回待处理而不是被拒绝的主要内容,如果未能解决你的问题,请参考以下文章
对不同类型的两个 Promise 使用相同的 then/catch/finally 块
在 mongodb 连接 url 中将“useNewUrlParser”和“useUnifiedTopology”设置为 true,避免了 promise 函数中的 catch 块