1. 简介
GraphQL是一种强大的API查询语言,它提供了一种灵活高效的数据交互方式。处理变更时,通常会在服务器上执行数据的更新或添加。然而,在某些情况下,我们可能需要在不返回任何数据的情况下进行变更。
在GraphQL中,默认行为是强制模式中的字段不可为空,这意味着字段必须始终返回值,并且除非明确标记为可空,否则不能为空。虽然这种严格性有助于提高API的清晰度和可预测性,但在某些情况下可能需要返回null。不过,通常认为避免返回null值是最佳做法。
在本文中,我们将探讨无需检索或返回特定信息即可实现GraphQL变异的技术。
2. 先决条件
对于我们的示例,我们需要以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-graphql</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Spring Boot GraphQL Starter提供了快速搭建GraphQL服务器的绝佳解决方案,通过利用自动配置并采用基于注解的编程方法,我们只需专注于编写服务所需的基本代码。
由于GraphQL与传输无关,我们在配置中包含了Web Starter,它利用Spring MVC通过HTTP暴露GraphQL API,我们可以通过默认的/graphql端点访问它。我们也可以使用其他Starter,例如Spring Webflux,以实现不同的底层实现。
3. 使用可空类型
与某些编程语言不同,GraphQL要求对模式中的每个字段进行显式声明,使其可空性。这种方法提高了清晰度,使我们能够明确地告知字段何时可能缺少值。
3.1 编写模式
Spring Boot GraphQL Starter会自动将GraphQL Schema文件定位到src/main/resources/graphql/**目录下,它会根据这些文件构建正确的结构,并将特殊的Bean注入到该结构。
我们将首先创建schema.graphqls文件,并为我们的示例定义模式:
type Post {
id: ID
title: String
text: String
category: String
author: String
}
type Mutation {
createPostReturnNullableType(title: String!, text: String!, category: String!, authorId: String!) : Int
}
我们将创建一个Post实体和一个用于创建新帖子的变更,此外,为了使我们的模式能够通过验证,它必须包含一个查询。因此,我们将实现一个返回帖子列表的虚拟查询:
type Query {
recentPosts(count: Int, offset: Int): [Post]!
}
3.2 使用Bean表示类型
在GraphQL服务器中,每个复杂类型都与一个Java Bean关联,这些关联是基于对象和属性名称建立的。因此,我们将为我们的帖子创建一个POJO类:
public class Post {
private String id;
private String title;
private String text;
private String category;
private String author;
// getters, setters, constructor
}
GraphQL模式中忽略了Java Bean上未映射的字段或方法,因此不会造成任何问题。
3.3 创建变异解析器
我们必须使用@MutationMapping注解标记处理函数,这些方法应该放在我们应用程序中的常规@Controller组件中,并在GraphQL应用程序中将这些类注册为数据修改组件:
@Controller
public class PostController {
List<Post> posts = new ArrayList<>();
@MutationMapping
public Integer createPost(@Argument String title, @Argument String text, @Argument String category, @Argument String author) {
Post post = new Post();
post.setId(UUID.randomUUID().toString());
post.setTitle(title);
post.setText(text);
post.setCategory(category);
post.setAuthor(author);
posts.add(post);
return null;
}
}
我们必须根据模式中的属性,使用@Argument注解方法的参数。在声明模式时,我们确定突变将返回Int类型,不带感叹号。这样,返回值就可以为null。
4. 创建自定义标量
在GraphQL中,标量是表示GraphQL查询或模式中的叶节点的原子数据类型。
4.1 标量和扩展标量
根据GraphQL规范,所有实现都必须包含以下标量类型:String、Boolean、Int、Float或ID。此外,graphql-java-extended-scalars还添加了更多自定义标量,例如Long、BigDecimal或LocalDate。但是,原始标量集和扩展标量集都没有针对空值的特殊标量,因此,我们将在本节中构建我们的标量。
4.2 创建自定义标量
要创建自定义标量,我们应该初始化一个GraphQLScalarType单例实例,我们将利用构建器设计模式来创建标量:
public class GraphQLVoidScalar {
public static final GraphQLScalarType Void = GraphQLScalarType.newScalar()
.name("Void")
.description("A custom scalar that represents the null value")
.coercing(new Coercing() {
@Override
public Object serialize(Object dataFetcherResult) {
return null;
}
@Override
public Object parseValue(Object input) {
return null;
}
@Override
public Object parseLiteral(Object input) {
return null;
}
})
.build();
}
标量的关键组件是名称、描述和强制转换。虽然名称和描述不言自明,但创建自定义标量的难点在于graphql.schema.Coercing的实现,该类负责三个功能:
- parseValue():接收变量输入对象并将其转换为相应的Java运行时表示。
- parseLiteral():接收AST文字graphql.language.Value作为输入,并将其转换为Java运行时表示。
- serialize():接收Java对象并将其转换为该标量的输出形状。
尽管对于复杂对象来说强制的实现可能非常复杂,但在我们的例子中,我们将为每种方法返回null。
4.3 注册自定义标量
我们首先创建一个配置类来注册我们的标量:
@Configuration
public class GraphQlConfig {
@Bean
public RuntimeWiringConfigurer runtimeWiringConfigurer() {
return wiringBuilder -> wiringBuilder.scalar(GraphQLVoidScalar.Void);
}
}
我们创建一个RuntimeWiringConfigurer Bean,用于配置GraphQL模式的运行时连接。在这个Bean中,我们使用RuntimeWiring类提供的scalar()方法来注册我们的自定义类型。
4.4 集成自定义标量
最后一步是将自定义标量集成到我们的GraphQL模式中,方法是使用定义的名称引用它。在本例中,我们只需在模式中声明标量Void即可使用它。
此步骤确保GraphQL引擎能够在整个模式中识别并使用我们的自定义标量,现在,我们可以将标量集成到我们的变更中:
scalar Void
type Mutation {
createPostReturnCustomScalar(title: String!, text: String!, category: String!, authorId: String!) : Void
}
此外,我们将更新映射的方法签名以返回我们的标量:
public Void createPostReturnCustomScalar(@Argument String title, @Argument String text, @Argument String category, @Argument String author)
5. 总结
在本文中,我们探索了如何在不返回特定数据的情况下实现GraphQL突变。我们演示了如何使用Spring Boot GraphQL Starter快速设置服务器,此外,我们还引入了一个自定义Void标量来处理空值,展示了如何扩展GraphQL的功能。
Post Directory
