Error handling 如何使用Apollo Server 2.0 GraphQL将模式指令中的信息包含为返回的数据?

Error handling 如何使用Apollo Server 2.0 GraphQL将模式指令中的信息包含为返回的数据?,error-handling,graphql,apollo-server,Error Handling,Graphql,Apollo Server,我正在使用模式指令对字段进行授权。Apollo服务器在解析程序返回后调用这些指令。由于这一点,指令无法访问输出,因此当授权失败时,如果没有复杂的解决方法抛出错误,我无法为用户包含相关信息,无论查询是否请求错误数据,最终总是返回错误数据 我希望有人比我更了解Apollo的内部结构,并指出我可以从指令中插入适当的信息,这样我就不必破坏GraphQL的标准功能 我尝试将我的输出包含在上下文中,但尽管指令具有访问权限,但这不起作用,因为数据已经从解析程序返回,之后不需要上下文版本 现在,我在指令中抛出一

我正在使用模式指令对字段进行授权。Apollo服务器在解析程序返回后调用这些指令。由于这一点,指令无法访问输出,因此当授权失败时,如果没有复杂的解决方法抛出错误,我无法为用户包含相关信息,无论查询是否请求错误数据,最终总是返回错误数据

我希望有人比我更了解Apollo的内部结构,并指出我可以从指令中插入适当的信息,这样我就不必破坏GraphQL的标准功能

我尝试将我的输出包含在上下文中,但尽管指令具有访问权限,但这不起作用,因为数据已经从解析程序返回,之后不需要上下文版本

现在,我在指令中抛出一个自定义错误和一个代码指令\错误,并包含我想要返回给用户的消息。在formatResponse函数中,我查找指令错误,并通过将它们传输到数据的内部错误数组来过滤错误数组。我知道formatResponse不是用来修改数据内容的,但据我所知,这是我唯一可以访问所需内容的地方。同样令人沮丧的是,响应中的错误对象没有包含错误中的所有字段

type User implements Node {
  id: ID!
  email: String @requireRole(requires: "error")
}

type UserError implements Error {
  path: [String!]!
  message: String!
}

type UserPayload implements Payload {
  isSuccess: Boolean!
  errors: [UserError]
  data: User
}

type UserOutput implements Output {
  isSuccess: Boolean!
  payload: [UserPayload]
}

/**
 * All output responses should be of format:
 * {
 *  isSuccess: Boolean
 *  payload: {
 *    isSuccess: Boolean
 *    errors: {
 *      path: [String]
 *      message: String
 *    }
 *    data: [{Any}]
 *  }
 * }
 */
const formatResponse = response => {
  if (response.errors) {
    response.errors = response.errors.filter(error => {
      // if error is from a directive, extract into errors
      if (error.extensions.code === "DIRECTIVE_ERROR") {
        const path = error.path;
        const resolverKey = path[0];
        const payloadIndex = path[2];

        // protect from null
        if (response.data[resolverKey] == null) {
          response.data[resolverKey] = {
            isSuccess: false,
            payload: [{ isSuccess: false, errors: [], data: null }]
          };
        } else if (
          response.data[resolverKey].payload[payloadIndex].errors == null
        ) {
          response.data[resolverKey].payload[payloadIndex].errors = [];
        }

        // push error into data errors array
        response.data[resolverKey].payload[payloadIndex].errors.push({
          path: [path[path.length - 1]],
          message: error.message,
          __typename: "DirectiveError"
        });
      } else {
        return error;
      }
    });

    if (response.errors.length === 0) {
      return { data: response.data };
    }
  }

  return response;
};
我对阿波罗行动顺序的理解是:

解析程序返回数据
根据查询参数筛选的数据?
在应用的对象/字段上调用指令
根据查询参数筛选的数据?
formatResponse有机会修改输出
formatError有机会修改错误
返回客户


我希望不必在指令中抛出错误,以便通过在formatResponse中提取信息来创建要传递给用户的信息。预期的结果是客户端只接收其请求的字段,但当前方法会将其打破,并返回数据错误和所有字段,无论客户端是否请求这些字段。

您可以使用析构函数将其插入:

const{SchemaDirectiveVisitor}=require(“阿波罗服务器快车”);
const{defaultFieldResolver}=require(“graphql”);
const=require(“lodash”);
类AuthDirective扩展了SchemaDirectiveVisitor{
visitFieldDefinition(字段){
const{resolve=defaultFieldResolver}=field;
field.resolve=异步函数(父函数、参数、上下文、信息){
//例如,您可以从标题中获取一些内容
//
//下面的验证是必要的,因为
//我的应用程序在本地和无服务器上运行
const authorization=u.has(上下文,“req”)
?context.req.headers.authorization
:context.headers.Authorization;
返回解析。应用(此[
父母亲
args,
{
上下文
用户:{授权,名称:,id:}
},
信息,
]);
};
}
}

然后在解析器上,您可以通过
上下文访问它。用户

您可以使用析构函数插入它:

const{SchemaDirectiveVisitor}=require(“阿波罗服务器快车”);
const{defaultFieldResolver}=require(“graphql”);
const=require(“lodash”);
类AuthDirective扩展了SchemaDirectiveVisitor{
visitFieldDefinition(字段){
const{resolve=defaultFieldResolver}=field;
field.resolve=异步函数(父函数、参数、上下文、信息){
//例如,您可以从标题中获取一些内容
//
//下面的验证是必要的,因为
//我的应用程序在本地和无服务器上运行
const authorization=u.has(上下文,“req”)
?context.req.headers.authorization
:context.headers.Authorization;
返回解析。应用(此[
父母亲
args,
{
上下文
用户:{授权,名称:,id:}
},
信息,
]);
};
}
}
然后在解析器上,您可以通过
context.user
访问它