GraphQL 突变 "Cannot set headers after they are sent to the client"

GraphQL mutation "Cannot set headers after they are sent to the client"

我正在实施 graphql 登录突变来验证用户登录凭据。 Mutation 使用 bcrypt 验证密码,然后向客户端发送一个 cookie,客户端将根据该 cookie 是买方用户还是所有者用户呈现用户配置文件)。

GraphQL登录突变代码:

const Mutation = new GraphQLObjectType({
    name: 'Mutation',
    fields: {
    loginUser: {
            type: UserType,
            args: {
                email: { type: GraphQLString },
                password: { type: GraphQLString }
            },
            resolve: function (parent, args, { req, res }) {
                User.findOne({ email: args.email }, (err, user) => {
                    if (user) {
                        bcrypt.compare(args.password, user.password).then(isMatch => {
                            if (isMatch) {
                                if (!user.owner) {
                                    res.cookie('cookie', "buyer", { maxAge: 900000, httpOnly: false, path: '/' });
                                } else {
                                    res.cookie('cookie', "owner", { maxAge: 900000, httpOnly: false, path: '/' });
                                }
                                return res.status(200).json('Successful login');
                            } else {
                                console.log('Incorrect password');
                            }
                        });
                    }
                });
            }
        }
     }
});

Server.js:

app.use("/graphql",
  (req, res) => {
    return graphqlHTTP({
      schema,
      graphiql: true,
      context: { req, res },
    })(req, res);
  });

错误信息:

(node:10630) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
[0]     at ServerResponse.setHeader (_http_outgoing.js:470:11)
[0]     at ServerResponse.header (/Users/xxx/xxx/server/node_modules/express/lib/response.js:771:10)
[0]     at ServerResponse.append (/Users/xxx/xxx/server/node_modules/express/lib/response.js:732:15)
[0]     at ServerResponse.res.cookie (/Users/xxx/xxx/server/node_modules/express/lib/response.js:857:8)
[0]     at bcrypt.compare.then.isMatch (/Users/xxx/xxx/server/schema/schema.js:89:41)

我已经对这个错误做了一些研究,但似乎找不到相关的答案。问题似乎在于响应主体被执行了不止一次,因此 "cannot set headers after they are sent to the client"。由于我同时发送 res.cookie() 和 res.status(200),我该如何解决这个问题?

express-graphql 已经设置状态并为您发送响应 - 无需在您的解析器中调用 res.statusres.json

GraphQL 总是 return 状态为 200,除非请求的查询无效,在这种情况下它 return 状态为 400。如果在执行请求时发生错误,它们将被包括在内响应(在与 returned data 分开的 errors 数组中),但状态仍为 200。这都是设计使然——请参阅其他讨论

您的解析器不应调用 res.json,而是应 return 适当类型的值(在本例中为 UserType),或将解析为该值的 Promise。

此外,您不应在解析器中使用回调,因为它们与 Promises 不兼容。如果您使用的 bcrypt 库支持使用 Promises,请使用适当的 API。如果没有,请切换到一个库(如 bcryptjs)或将您的回调包装在 Promise 中。对于您使用的任何 ORM,同上。

最后,您的解析器应如下所示:

resolve: function (parent, args, { req, res }) {
  const user = await User.findOne({ email: args.email })
  if (user) {
    const isMatch = await bcrypt.compare(args.password, user.password)
    if (isMatch) {
      const cookieValue = user.owner ? 'owner' : 'buyer'
      res.cookie('cookie', cookieValue, { maxAge: 900000, httpOnly: false, path: '/' })
      return user
    }
  }
  // If you want an error returned in the response, just throw it
  throw new Error('Invalid credentials')
}