当前位置: 首页 > 知识库问答 >
问题:

我可以安全地从GraphQLResolver回调GraphQLQueryResolver吗?

卫高明
2023-03-14

我的问题是:在实现解析方法时,什么是最好的?直接调用数据存储库或回调主解析器(即实现GraphQLQueryResolver)的解析器(前提是它具有适当的方法)?换句话说(参见下面的示例),在回调主解析器时,DataFetchingEnvironment是否正确调整/设置?

注意:如果您不熟悉Resolvers如何使用GraphQL Java工具,我就让您看看@https://www.graphql-java-kickstart.com/tools/schema-definition/

现在来举个例子。

在Spring Boot应用程序中,使用GraphQL Java工具(使用GraphQL Spring Boot starter依赖项),让我们使用以下模式:

type User {
  id: ID
  name: String
  company: Company
}

type Company {
  id: ID
  name: String
}

使用匹配的POJO或实体(省略getter/setter):

java prettyprint-override">class User {

  private Long id;
  private String name;
  private Long idCompany;

}

class Company {

  private Long id;
  private String name;

}

这些解析器(注意:UserRepository和CompanyRepository是您常用的DAO/Repository类,由Spring数据(JPA)支持,或者由其他东西支持,或者由您自己的自定义实现支持,等等……):

QueryResolver implements GraphQLQueryResolver {

  @Autowired
  private UserRepository userRepository;

  @Autowired
  private CompanyRepository companyRepository;

  public User user(String id) {
    return userRepository.findById(id);
  }

  public Company company(String idCompany) {
    return companyRepository.findById(idCompany);
  }

}

UserResolver implements GraphQLResolver<User> {

  @Autowired
  private CompanyRepository companyRepository;

  public Company company(User user) {
    return companyRepository.findById(user.getIdCompany());
  }

  // ...or should I do:

  @Autowired
  private QueryResolver queryResolver;

  public Company company(User user) {
    return queryResolver.company(user.getIdCompany());
  }

}

当在每个方法的末尾添加DataFetchingEnvironment environment并在执行对各种(数据)存储库的调用之前使用它时,这(更)有意义。

继续上面的示例,这样做是否正确(即,DataFetchingEnvironment在再次传输到主查询解决程序时是否正确填充)?

UserResolver implements GraphQLResolver<User> {

  @Autowired
  private QueryResolver queryResolver;

  public Company company(User user, DataFetchingEnvironment environment) {
    return queryResolver.company(user.getIdCompany(), environment);
  }

}

共有1个答案

郎正初
2023-03-14

您可以将解析程序调用委托给服务层,但不要在解析程序/服务之间传递DataFecthingEnvironment。它将无法正确填充。

这是不安全的,可能会导致难以确定的错误和数据丢失。

DataFetchingEnvironment是从正在执行的graphql查询/变异填充的,您希望解析器方法中的DataFetchingEnvironment与正在调用的解析器方法一致。

考虑下面的模式:

type Movie {
  id: ID!
  title: String!
  rating: String
  actors: [Actor]
}

type Actor {
  id: ID!
  name: String!
  role: String
}

input ActorUpdateInput {
  id: ID!
  name: String
  role: String
}

type Query {
  #Search movies with a specified Rating
  searchMovie(name: movieTitle, rating: String): Book
  #Search R-rated movies
  searchRRatedMovie(name: movieTitle): Book
}

type Mutation {
  #Update a movie and its actors
  updateMovie(id:Id!, title: String, actors: [ActorUpdateInput]): Movie
  #Update an actor
  updateActor(input: ActorUpdateInput!): Actor
}
query {
  searchRRatedMovie(name: "NotRRatedMovie") {
    title
  }
}

电影“NotRRatedMovie”未分级,我们可以预期此查询将返回空数据。

现在,下面的实现将DataFetchingEnvironment从searchRRatedMovie传递到searchMovie查询解析器实现。

public class QueryResolver  {

  @Autowired
  MovieRepository repository;

  public Movie searchRRatedMovie(String title, DataFetchingEnvironment environment) {
    return this.searchMovie(name, "R", environment);
  }

  public Movie searchMovie(String title, String rating, DataFetchingEnvironment environment) {
    if(!environment.containsArgument("rating")) {
      //if the rating argument was omitted from the query
      return repository.findByTitle(title);
    } else if(rating == null) {
      //rating is an argument but was set to null (ie. the user wants to retrieve all the movies without any rating)
      return repository.findByTitleAndRating(title, null);
    } else {
      repository.findByNameAndTitle(name,rating);
    }
  }

}

这看起来不错,但查询不会返回null。

第一个解析器将调用searchRRatedMovie(“NotRRatedMovie”,environment)。该环境不包含“rating”参数。到达以下行时:if(!environment.containsArgument(“rating”){参数不存在,它将进入if语句,返回repository.findbytile(“NotRRatedMovie”)而不是预期的repository.findbytileandrating(“NotRRatedMovie”,“R”)

我们可以使用DataFetchingEnvironment参数在变异中实现部分更新:如果一个参数是null,我们需要DataFetchingEnvironment参数来告诉我们该参数是null,因为它被设置为null(即变异应该将基础值更新为null),还是因为根本没有设置(即突变不应更新基础值)。

public class MutationResolver  {

  @Autowired
  MovieRepository movieRepository;

  @Autowired
  ActorRepository actorRepository;

  public Movie updateMovie(Long id, String title, List<ActorUpdateInput> actors, DataFetchingEnvironment environment) {
    Movie movie = movieRepository.findById(id);

    //Update the title if the "title" argument is set
    if(environment.containsArgument("title")) {
      movie.setTitle(title);
    }

    if(environment.containsArgument("actors")) {
      for(ActorUpdateInput actorUpdateInput : actors) {
        //The passing the environment happens here
        this.updateActor(actorUpdateInput, environment);
      }
    }
    return movie;
  }

  public Actor updateActor(ActorUpdateInput input, DataFetchingEnvironment environment) {
    Actor actor = actorRepository.findById(input.getId());

    //We retrieve the argument "input". It is a Map<String, Object> where keys are arguments of the ActorUpdateInput
    Map<String, Object> actorArguments = (Map<String, Object>) env.getArguments().get("input");
  
    //Problem: if the environment was passed from updateMovie, it does not contains an "input" parameter! actorArguments is now null and the following code will fail

    //Update the actor name if the "name" argument is set
    if (actorArguments.containsKey("name")) {
      actor.setName(input.getName());
    }

    //Update the actor role if the "role" argument is set
    if (actorArguments.containsKey("role")) {
      actor.setRole(input.getRole());
    }
    return actor;
  }

}

这里updateActor解析器需要一个输入参数(与updateActor突变定义匹配)。因为我们传递了一个错误填充的环境,所以实现中断了。

不带DataFetchinEnvironment的部分更新

如果要实现部分更新,可以不使用DataFecthingEnvironment来实现,正如我在本评论中所做的:https://github.com/graphql-java-kickstart/graphql-java-tools/issues/141#issuecomment-560938020

在将DataFetchingEnvironment传递给下一个冲突解决程序之前重新生成它

如果您确实需要DataFetchingEnvironment,您仍然可以构建一个新的环境来传递给下一个解析器。这可能会更困难,更容易出错,但您可以看看原始DataFetchingEnvironment是如何在ExecutionStrategy中创建的。JAVAhttps://github.com/graphql-java/graphql-java/blob/master/src/main/java/graphql/execution/ExecutionStrategy.java#L246

 类似资料:
  • 问题内容: 能后可重复使用被称为? 这play.golang.org/p/QLsvA-b4Ae按预期运行,但它保证是安全的吗?文档没有这么说,但也许我只是偏执。 问题答案: 是的,这很安全。实际上,它比这更安全。您可以同时从多个goroutine中进行选择,并可以根据您的用例进行适当的互换和调用。只要发生在之前,您就应该是安全的。 出于好奇,现在使用互斥锁,两个int32s计数器和一个信号量来实现

  • 问题内容: 我已经使用Python多处理模块在Monte Carlo代码中实现了一些简单的并行性。我有看起来像的代码: 但是,当我查看结果列表时,似乎蒙特卡洛迭代器尚未启动。我知道它们有,因为我可以让这些过程在蒙特卡洛步骤中打印出信息。所以我在做些愚蠢的事情。我以为job.join()会阻止结果列表被构建,直到一切运行完毕,因此mc.results字段将被更新。 我意识到我还没有告诉您我的Mont

  • 问题内容: 我知道jython允许我们从任何Java的类文件中调用Java方法,就好像它们是为python编写的一样,但是相反的可能吗? 我已经有很多用python编写的算法,它们在python和jython上都可以很好地工作,但是它们缺少适当的GUI。我计划将GUI与Jav​​a一起使用,并保持python库完整。我无法使用jython或python编写良好的GUI,也无法使用python编写良

  • 问题内容: 我正在设计一个最适合我的用途,而不是使用成熟的PHP MVC。我已经完成了基本框架,并编写了运行网站所需的模型和控制器。 现在,我进入“视图”,遇到了一个小难题。我的方法对我来说很好,但是为了将来参考,我想知道我在做什么是否有不好的习惯。 我正在尝试做的是: 在我的视图中,我正在调用一个运行身份验证系统的模型,并请求用户的登录状态。然后,我使用该布尔值来决定是否在视图中显示某些元素,以

  • 问题内容: 如何通过 JNI 从Node.js调用Java ?有没有例子? 问题答案: 看起来很棘手。Node.JS在Google Chrome JavaScript引擎V8上运行。您将要做的是创建一个V8 C ++绑定(一个v8 c ++崩溃课程 显示了一个示例),该绑定启动JVM并执行所有JNI处理。 我认为让JavaServer和Node.js通过网络进行通信可能更好(有人写了一个使用Rab

  • 问题内容: 我需要开发一个可长期离线运行的Web应用程序。为了使它可行,我无法避免将敏感数据(个人数据,而不是您将仅存储散列数据的类型)保存在本地存储中。 我接受不建议这样做,但是我几乎没有选择要执行以下操作来保护数据: 使用斯坦福JavaScript加密库和AES-256将所有内容都加密到本地存储中 用户密码是加密密钥,未存储在设备上 通过ssl从单个受信任的服务器提供所有内容(在线时) 使用o