简短的回答
您可以将解析器调用委托给服务层,但不要在解析器/服务之间传递 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
}
示例 1:查询
query {
searchRRatedMovie(name: "NotRRatedMovie") {
title
}
}
电影“NotRRatedMovie”不是 R 级的,我们可以期望这个查询返回一个空数据。
现在,下面的实现将来自 searchRRatedMovie 的 DataFetchingEnvironment 传递给 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")) {
到达该行时:"rating"
参数不存在,它将进入 if 语句,返回repository.findByTitle("NotRRatedMovie")
而不是预期的repository.findByTitleAndRating("NotRRatedMovie","R")
.
示例 2:具有部分更新的变异
我们可以使用 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.java https://github.com/graphql-java/graphql-java/blob/master/src/main中创建的/java/graphql/execution/ExecutionStrategy.java#L246