GraphQL突变“;发送到客户机后无法设置标题";
我正在实现graphql登录变异来验证用户登录凭证。使用bcrypt验证密码,然后向客户端发送cookie,客户端将根据cookie是买家还是所有者用户呈现用户配置文件) GraphQL登录代码:GraphQL突变“;发送到客户机后无法设置标题";,graphql,response-headers,express-graphql,graphql-mutation,Graphql,Response Headers,Express Graphql,Graphql Mutation,我正在实现graphql登录变异来验证用户登录凭证。使用bcrypt验证密码,然后向客户端发送cookie,客户端将根据cookie是买家还是所有者用户呈现用户配置文件) GraphQL登录代码: const Mutation = new GraphQLObjectType({ name: 'Mutation', fields: { loginUser: { type: UserType, args: {
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');
}
});
}
});
}
}
}
});
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)
Server.js:
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');
}
});
}
});
}
}
}
});
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)
错误消息:
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');
}
});
}
});
}
}
}
});
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)
我对这个错误做了一些研究,但似乎找不到相关的答案。问题似乎存在于多次执行的响应体中,因此“在发送到客户端后无法设置头”。既然我同时发送res.cookie()和res.status(200),那么我如何解决这个问题呢?
express graphql
已经为您设置了状态并发送了一个响应——无需在解析器中调用res.status
或res.json
GraphQL始终返回状态200,除非请求的查询无效,在这种情况下,它返回状态400。如果在执行请求时发生错误,它们将包含在响应中(在与返回的数据
分开的错误
数组中),但状态仍将为200。这一切都是出于设计——请参阅附加讨论
解析程序应该返回适当类型的值(在本例中为UserType
)或将解析为该值的承诺,而不是调用res.json
此外,您不应该在解析器中使用回调,因为它们与承诺不兼容。如果您使用的bcrypt库支持使用承诺,请使用适当的API。如果没有,请切换到一个库,它可以(像)或将您的回调封装在一个承诺中。无论您使用什么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')
}
express graphql
已经为您设置了状态并发送了响应——无需在解析器内调用res.status
或res.json
GraphQL始终返回状态200,除非请求的查询无效,在这种情况下,它返回状态400。如果在执行请求时发生错误,它们将包含在响应中(在与返回的数据
分开的错误
数组中),但状态仍将为200。这一切都是出于设计——请参阅附加讨论
解析程序应该返回适当类型的值(在本例中为UserType
)或将解析为该值的承诺,而不是调用res.json
此外,您不应该在解析器中使用回调,因为它们与承诺不兼容。如果您使用的bcrypt库支持使用承诺,请使用适当的API。如果没有,请切换到一个库,它可以(像)或将您的回调封装在一个承诺中。无论您使用什么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')
}
很好,我现在可以设置cookie了,它验证了我的登录。同样感谢代码清理。@jche如果是同一个错误,您可能仍然有一个回调或一个未正确链接的承诺。如果你已经删除了所有的回调,但仍然无法找到它,请随时用更新的代码更新你的原始帖子,我可以接受一个gander。是的,很抱歉,我忘记了删除res.status(200)。现在可以了,谢谢。太好了,我现在可以设置cookie了,它验证了我的登录。同样感谢代码清理。@jche如果是同一个错误,您可能仍然有一个回调或一个未正确链接的承诺。如果你已经删除了所有的回调,但仍然无法找到它,请随时用更新的代码更新你的原始帖子,我可以接受一个gander。是的,很抱歉,我忘记了删除res.status(200)。现在可以用了,谢谢。