GraphQL:field.resolve() 结果在指令内未定义
GraphQL: field.resolve() result is undefined inside directive
我一直在关注我在 Google 上找到的关于如何制作 AuthDirective 的所有结果,但我无法让它发挥作用。
我会留下所有文件,看看我是否在实现时出错,但我猜不是,因为我在 field.resolve.
中没有任何条件的情况下进行了测试
我发现的一个特别的事情是 field.resolve,带有 4 个参数,第一个参数总是未定义的,不确定是否可以,但作为一个额外的提示。
所以这是代码:
这是我的类型定义的一部分
directive @isAuthenticated on FIELD_DEFINITION
scalar Date
type User {
id: ID,
name: String
email: String
password: String
createdAt: Date
}
type Token {
token: String
}
# IF I REMOVE @isAuthenticated this works!
type Query {
user(name: String): User! @isAuthenticated
users: [User!]! @isAuthenticated
}
type Mutation {
createUser(name: String, email: String, password: String): Boolean!
login(email: String!, password: String!): Token!
}
这是我的架构:
import path from 'path';
import { makeExecutableSchema } from 'graphql-tools';
import { fileLoader, mergeResolvers, mergeTypes } from 'merge-graphql-schemas';
import schemaDirectives from './Directives';
const typedefsArray = fileLoader(path.join(__dirname, './**/*.graphql'));
const resolversArray = fileLoader(path.join(__dirname, './**/*.resolvers.ts'));
const typeDefs = mergeTypes(typedefsArray);
const resolvers = mergeResolvers(resolversArray);
const schema = makeExecutableSchema({ typeDefs, resolvers, schemaDirectives });
export default schema;
这是我的 AuthDirective
import { defaultFieldResolver } from 'graphql';
import { AuthenticationError, SchemaDirectiveVisitor } from 'apollo-server-express';
class AuthDirective extends SchemaDirectiveVisitor {
visitFieldDefinition(field: any) {
const { resolver = defaultFieldResolver } = field;
field.resolve = async function (...args: any) {
const [_, __, context] = args;
const { req } = context;
const accessToken = req.headers.authorization
? req.headers.authorization.replace('Bearer ', '')
: null;
if (accessToken) {
// here I was doing jwt.verify(accessToken) but I removed it for now to simplify
const result = await resolver.apply(this, args);
return result; // <--- THIS IS ALWAYS UNDEFINED
} else {
throw new AuthenticationError('Unauthorized field');
}
};
}
}
export default AuthDirective;
它位于 ./Directives 下并从索引中导出为:
import AuthDirective from './auth';
export default {
isAuthenticated: AuthDirective
}
最后是我的用户解析器。就像我之前说的,如果我从用户 graphql 查询中删除指令,它就会正常工作:
import jwt from 'jsonwebtoken';
import { AuthenticationError, UserInputError } from 'apollo-server-express';
import { IUser } from './IUser';
import UserModel from './user.schema';
import { Config } from '../../config';
const { APP_SECRET } = Config;
const createToken = async (user: IUser, secret: string, expiresIn: string) => {
const { id, email, name } = user;
return jwt.sign({ id, email, name }, secret, { expiresIn });
};
const userResolvers = {
Query: {
user: (_: any, { name }: { name: string }) => UserModel.findOne({ name }).exec(),
users: () => UserModel.find({}).exec()
},
Mutation: {
createUser: async (_: any, args: IUser) => {
await new UserModel(args).save();
return true;
},
login: async (_: any, { email, password }: IUser) => {
const user = await UserModel.findOne({ email }).exec();
if (!user) {
throw new UserInputError('No user found with this login credentials.');
}
const isValid = await user.validatePassword(password);
if (!isValid) {
throw new AuthenticationError('Invalid password.');
}
return { token: createToken(user, APP_SECRET, '30m') };
}
}
};
export default userResolvers;
我的服务器:
import morgan from 'morgan';
import express from 'express';
import bodyParser from 'body-parser';
import compression from 'compression';
import depthLimit from 'graphql-depth-limit';
import { ApolloServer } from 'apollo-server-express';
import { Config, DBConfig } from './config';
import schema from './domain';
const { PORT } = Config;
const app = express();
const apolloServer = new ApolloServer({
schema,
validationRules: [depthLimit(7)],
context: ({ req, res }: any) => ({ req, res })
});
app.use(express.json());
app.use(compression());
app.use(morgan('combined'));
app.use(bodyParser.urlencoded({ extended: true }));
apolloServer.applyMiddleware({ app });
DBConfig.init();
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
field
上没有resolver
属性,所以这个
const { resolver = defaultFieldResolver } = field;
将始终导致 resolver
具有 defaultFieldResolver
的值。如果您在某个具有自定义解析器的字段上使用指令,则永远不会调用该自定义解析器——仅使用默认行为,这确实可以 return undefined
.
您需要确保您使用的是正确的 属性:
const { resolve = defaultFieldResolver } = field;
或者你可以在解构时重命名结果变量 field
:
const { resolve: resolver = defaultFieldResolver } = field;
我一直在关注我在 Google 上找到的关于如何制作 AuthDirective 的所有结果,但我无法让它发挥作用。
我会留下所有文件,看看我是否在实现时出错,但我猜不是,因为我在 field.resolve.
中没有任何条件的情况下进行了测试我发现的一个特别的事情是 field.resolve,带有 4 个参数,第一个参数总是未定义的,不确定是否可以,但作为一个额外的提示。
所以这是代码:
这是我的类型定义的一部分
directive @isAuthenticated on FIELD_DEFINITION
scalar Date
type User {
id: ID,
name: String
email: String
password: String
createdAt: Date
}
type Token {
token: String
}
# IF I REMOVE @isAuthenticated this works!
type Query {
user(name: String): User! @isAuthenticated
users: [User!]! @isAuthenticated
}
type Mutation {
createUser(name: String, email: String, password: String): Boolean!
login(email: String!, password: String!): Token!
}
这是我的架构:
import path from 'path';
import { makeExecutableSchema } from 'graphql-tools';
import { fileLoader, mergeResolvers, mergeTypes } from 'merge-graphql-schemas';
import schemaDirectives from './Directives';
const typedefsArray = fileLoader(path.join(__dirname, './**/*.graphql'));
const resolversArray = fileLoader(path.join(__dirname, './**/*.resolvers.ts'));
const typeDefs = mergeTypes(typedefsArray);
const resolvers = mergeResolvers(resolversArray);
const schema = makeExecutableSchema({ typeDefs, resolvers, schemaDirectives });
export default schema;
这是我的 AuthDirective
import { defaultFieldResolver } from 'graphql';
import { AuthenticationError, SchemaDirectiveVisitor } from 'apollo-server-express';
class AuthDirective extends SchemaDirectiveVisitor {
visitFieldDefinition(field: any) {
const { resolver = defaultFieldResolver } = field;
field.resolve = async function (...args: any) {
const [_, __, context] = args;
const { req } = context;
const accessToken = req.headers.authorization
? req.headers.authorization.replace('Bearer ', '')
: null;
if (accessToken) {
// here I was doing jwt.verify(accessToken) but I removed it for now to simplify
const result = await resolver.apply(this, args);
return result; // <--- THIS IS ALWAYS UNDEFINED
} else {
throw new AuthenticationError('Unauthorized field');
}
};
}
}
export default AuthDirective;
它位于 ./Directives 下并从索引中导出为:
import AuthDirective from './auth';
export default {
isAuthenticated: AuthDirective
}
最后是我的用户解析器。就像我之前说的,如果我从用户 graphql 查询中删除指令,它就会正常工作:
import jwt from 'jsonwebtoken';
import { AuthenticationError, UserInputError } from 'apollo-server-express';
import { IUser } from './IUser';
import UserModel from './user.schema';
import { Config } from '../../config';
const { APP_SECRET } = Config;
const createToken = async (user: IUser, secret: string, expiresIn: string) => {
const { id, email, name } = user;
return jwt.sign({ id, email, name }, secret, { expiresIn });
};
const userResolvers = {
Query: {
user: (_: any, { name }: { name: string }) => UserModel.findOne({ name }).exec(),
users: () => UserModel.find({}).exec()
},
Mutation: {
createUser: async (_: any, args: IUser) => {
await new UserModel(args).save();
return true;
},
login: async (_: any, { email, password }: IUser) => {
const user = await UserModel.findOne({ email }).exec();
if (!user) {
throw new UserInputError('No user found with this login credentials.');
}
const isValid = await user.validatePassword(password);
if (!isValid) {
throw new AuthenticationError('Invalid password.');
}
return { token: createToken(user, APP_SECRET, '30m') };
}
}
};
export default userResolvers;
我的服务器:
import morgan from 'morgan';
import express from 'express';
import bodyParser from 'body-parser';
import compression from 'compression';
import depthLimit from 'graphql-depth-limit';
import { ApolloServer } from 'apollo-server-express';
import { Config, DBConfig } from './config';
import schema from './domain';
const { PORT } = Config;
const app = express();
const apolloServer = new ApolloServer({
schema,
validationRules: [depthLimit(7)],
context: ({ req, res }: any) => ({ req, res })
});
app.use(express.json());
app.use(compression());
app.use(morgan('combined'));
app.use(bodyParser.urlencoded({ extended: true }));
apolloServer.applyMiddleware({ app });
DBConfig.init();
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
field
上没有resolver
属性,所以这个
const { resolver = defaultFieldResolver } = field;
将始终导致 resolver
具有 defaultFieldResolver
的值。如果您在某个具有自定义解析器的字段上使用指令,则永远不会调用该自定义解析器——仅使用默认行为,这确实可以 return undefined
.
您需要确保您使用的是正确的 属性:
const { resolve = defaultFieldResolver } = field;
或者你可以在解构时重命名结果变量 field
:
const { resolve: resolver = defaultFieldResolver } = field;