5. 带注释的控制器

Spring for GraphQL 提供了一个基于注释的编程模型,其中组件使用注释来声明具有灵活方法签名的处理程序方法,以 获取特定 GraphQL 字段的数据。例如:@Controllerspring-doc.cn

@Controller
public class GreetingController {

        @QueryMapping (1)
        public String hello() { (2)
            return "Hello, world!";
        }

}
1 将此方法绑定到查询,即 Query 类型下的字段。
2 如果未在注释上声明,则从方法名称确定查询。

Spring for GraphQL 用于将上述处理程序方法注册为名为 “hello” 的查询。RuntimeWiring.Buildergraphql.schema.DataFetcherspring-doc.cn

5.1. 声明

您可以将 bean 定义为标准的 Spring bean 定义。构造型允许自动检测,与 Spring 通用 支持 Detecting 和 类 为它们自动注册 bean 定义。它还充当带注释的 类,指示其在 GraphQL 应用程序中作为数据获取组件的角色。@Controller@Controller@Controller@Componentspring-doc.cn

AnnotatedControllerConfigurer检测 bean 并注册其 带注释的处理程序方法为 S 通过 .它是一个 的实现可以添加到 . Boot Starter 自动声明为 bean 并将所有 bean 添加到 和 中,这将启用 支持带注释的 s,请参阅 GraphQL RuntimeWiring 部分 在 Boot Starter 文档中。@ControllerDataFetcherRuntimeWiring.BuilderRuntimeWiringConfigurerGraphQlSource.BuilderAnnotatedControllerConfigurerRuntimeWiringConfigurerGraphQlSource.BuilderDataFetcherspring-doc.cn

5.2.@SchemaMapping

注释将处理程序方法映射到 GraphQL 架构中的字段 并声明它是该字段的 。注解可以指定 父类型名称,以及字段名称:@SchemaMappingDataFetcherspring-doc.cn

@Controller
public class BookController {

    @SchemaMapping(typeName="Book", field="author")
    public Author getAuthor(Book book) {
        // ...
    }
}

注解也可以省略这些属性,在这种情况下, field name 默认为 method name,而 type name 默认为 simple 类 注入方法的源/父对象的名称。例如,下面的 默认为 type “Book” 和字段 “author”:@SchemaMappingspring-doc.cn

@Controller
public class BookController {

    @SchemaMapping
    public Author author(Book book) {
        // ...
    }
}

可以在类级别声明 Comments 以指定默认值 键入类中所有处理程序方法的 name。@SchemaMappingspring-doc.cn

@Controller
@SchemaMapping(typeName="Book")
public class BookController {

    // @SchemaMapping methods for fields of the "Book" type

}

@QueryMapping、 和 是元注释,它们 本身带有 Comments ,并且 typeName 分别预设为 、 或 。实际上,这些是快捷方式注释 for 字段 Query, Mutation 和 Subscription 类型。例如:@MutationMapping@SubscriptionMapping@SchemaMappingQueryMutationSubscriptionspring-doc.cn

@Controller
public class BookController {

    @QueryMapping
    public Book bookById(@Argument Long id) {
        // ...
    }

    @MutationMapping
    public Book addBook(@Argument BookInput bookInput) {
        // ...
    }

    @SubscriptionMapping
    public Flux<Book> newPublications() {
        // ...
    }
}

@SchemaMapping处理程序方法具有灵活的签名,并且可以从一系列 方法参数和返回值..spring-doc.cn

5.2.1. 方法签名

架构映射处理程序方法可以具有以下任何方法参数:spring-doc.cn

方法参数 描述

@Argumentspring-doc.cn

用于访问绑定到更高级别类型化 Object 的命名字段参数。 请参阅 @Argumentspring-doc.cn

@Argument Map<String, Object>spring-doc.cn

对于参数的原始映射的访问,where 没有属性。@Argumentnamespring-doc.cn

ArgumentValuespring-doc.cn

用于访问绑定到更高级别的类型化 Object 的命名字段参数 带有一个标志,用于指示是否省略了 input 参数,而不是设置为 。 请参阅 ArgumentValuenullspring-doc.cn

@Argumentsspring-doc.cn

用于访问绑定到更高级别类型化 Object 的所有字段参数。 请参阅 @Argumentsspring-doc.cn

@Arguments Map<String, Object>spring-doc.cn

用于访问参数的原始映射。spring-doc.cn

@ProjectedPayload接口spring-doc.cn

用于通过项目接口访问字段参数。 请参阅 @ProjectedPayload 界面spring-doc.cn

“来源”spring-doc.cn

用于访问字段的源(即父级/容器)实例。 请参阅源代码spring-doc.cn

DataLoaderspring-doc.cn

要访问 . 请参阅 DataLoaderDataLoaderDataLoaderRegistryspring-doc.cn

@ContextValuespring-doc.cn

要从 中的主 访问属性。GraphQLContextDataFetchingEnvironmentspring-doc.cn

@LocalContextValuespring-doc.cn

要从 中的本地访问属性。GraphQLContextDataFetchingEnvironmentspring-doc.cn

GraphQLContextspring-doc.cn

要从 .DataFetchingEnvironmentspring-doc.cn

java.security.Principalspring-doc.cn

从 Spring Security 上下文获取(如果可用)。spring-doc.cn

@AuthenticationPrincipalspring-doc.cn

用于从 Spring Security 上下文访问。Authentication#getPrincipal()spring-doc.cn

DataFetchingFieldSelectionSetspring-doc.cn

要通过 .DataFetchingEnvironmentspring-doc.cn

Locale,Optional<Locale>spring-doc.cn

要从 访问 。LocaleDataFetchingEnvironmentspring-doc.cn

DataFetchingEnvironmentspring-doc.cn

要直接访问底层 .DataFetchingEnvironmentspring-doc.cn

架构映射处理程序方法可以返回:spring-doc.cn

  • 任何类型的 resolved 值。spring-doc.cn

  • Mono以及 asynchronous value(s)。支持控制器方法和 any 中,如 Reactive DataFetcher 中所述。FluxDataFetcherspring-doc.cn

  • java.util.concurrent.Callable以异步方式生成值。 要使其正常工作,必须配置一个 .AnnotatedControllerConfigurerExecutorspring-doc.cn

5.2.2.@Argument

在 GraphQL Java 中,提供对特定于字段的映射的访问 argument 值。这些值可以是简单的标量值(例如 String、Long)、a 或 values 用于更复杂的输入,或 a of 值。DataFetchingEnvironmentMapListspring-doc.cn

使用注释将参数绑定到目标对象,并使用 注入到 handler 方法中。通过将参数值映射到 primary data 构造函数,或者使用默认的 constructor 创建对象,然后将 argument 值映射到其属性。这是 递归重复,使用所有嵌套参数值并创建嵌套目标对象 因此。例如:@Argumentspring-doc.cn

@Controller
public class BookController {

    @QueryMapping
    public Book bookById(@Argument Long id) {
        // ...
    }

    @MutationMapping
    public Book addBook(@Argument BookInput bookInput) {
        // ...
    }
}

默认情况下,如果方法参数名称可用(需要编译器 标志替换为 Java 8+ 或编译器中的调试信息),它用于查找参数。 如果需要,您可以通过 annotation 自定义名称,例如 .-parameters@Argument("bookInput")spring-doc.cn

注解没有 “required” 标志,也没有 指定 Default (默认值)。这两者都可以在 GraphQL 架构级别指定,并且 由 GraphQL Java 强制执行。@Argument

如果绑定失败,则会引发 a,并将绑定问题累积为字段 errors,其中 of each error 是出现问题的参数路径。BindExceptionfieldspring-doc.cn

您可以与参数一起使用,以获取 all 参数值。不得设置 name 属性 on。@ArgumentMap<String, Object>@Argumentspring-doc.cn

5.2.3.ArgumentValue

默认情况下,GraphQL 中的输入参数是可为 null 的可选参数,即参数 可以设置为 literal,或者根本不提供。这种区别对于 使用更改进行部分更新,其中基础数据也可能相应地设置为 或 根本不更改。使用 @Argument 时,没有办法进行这样的区分,因为在这两种情况下你都会得到 OR AN 空。nullnullnullOptionalspring-doc.cn

如果你不想知道某个值是否根本没有提供,你可以声明一个 method 参数,它是结果值的简单容器。 以及一个标志,以指示是否完全省略了 input 参数。你 可以使用 this 而不是 ,在这种情况下,参数名称由 方法参数名称,或与 一起指定参数名称。ArgumentValue@Argument@Argumentspring-doc.cn

例如:spring-doc.cn

@Controller
public class BookController {

    @MutationMapping
    public void addBook(ArgumentValue<BookInput> bookInput) {
        if (!bookInput.isOmitted()) {
            BookInput value = bookInput.value();
            // ...
        }
    }
}

ArgumentValue还支持作为方法参数的对象结构中的字段,通过 constructor 参数或 setter 初始化,包括 作为嵌套在顶级对象下任何级别的对象的字段。@Argumentspring-doc.cn

5.2.4.@Arguments

如果要将完整的参数映射绑定到单个 target Object 的 ,而 则绑定特定的命名参数。@Arguments@Argumentspring-doc.cn

例如,使用参数 “bookInput” 的值 来初始化 ,while 使用完整的参数映射,并在其中 case 时,顶级参数将绑定到 properties。@Argument BookInput bookInputBookInput@ArgumentsBookInputspring-doc.cn

您可以与参数一起使用,以获取 all 参数值。@ArgumentsMap<String, Object>spring-doc.cn

5.2.5. 界面@ProjectedPayload

作为将完整 Objects 与 @Argument 结合使用的替代方法, 您还可以使用投影接口通过 定义明确的最小接口。当 Spring Data 位于 class path 上时,参数投影由 Spring Data 的接口投影提供。spring-doc.cn

要使用此功能,请创建一个带有 Comments 并声明 it 作为控制器方法参数。如果参数用 , 它适用于 Map 中的单个参数。当声明时没有 ,投影适用于 完整的 arguments 映射。@ProjectedPayload@ArgumentDataFetchingEnvironment.getArguments()@Argumentspring-doc.cn

例如:spring-doc.cn

@Controller
public class BookController {

    @QueryMapping
    public Book bookById(BookIdProjection bookId) {
        // ...
    }

    @MutationMapping
    public Book addBook(@Argument BookInputProjection bookInput) {
        // ...
    }
}

@ProjectedPayload
interface BookIdProjection {

    Long getId();
}

@ProjectedPayload
interface BookInputProjection {

    String getName();

    @Value("#{target.author + ' ' + target.name}")
    String getAuthorAndName();
}

5.2.6. 源码

在 GraphQL Java 中,它提供对源(即 parent/container) 实例。要访问它,只需声明一个 method 参数 的预期目标类型。DataFetchingEnvironmentspring-doc.cn

@Controller
public class BookController {

    @SchemaMapping
    public Author author(Book book) {
        // ...
    }
}

source method 参数还有助于确定 Map 的类型名称。 如果 Java 类的简单名称与 GraphQL 类型匹配,则无需 在 Annotation 中显式指定 type name。@SchemaMappingspring-doc.cn

@BatchMapping 处理程序方法可以批量加载查询的所有作者。 给定 Source/Parent Books 对象的列表。spring-doc.cn

5.2.7.DataLoader

当您为实体注册批量加载函数时,如批量加载中所述,您可以通过声明 method 参数,并使用它来加载实体:DataLoaderDataLoaderspring-doc.cn

@Controller
public class BookController {

    public BookController(BatchLoaderRegistry registry) {
        registry.forTypePair(Long.class, Author.class).registerMappedBatchLoader((authorIds, env) -> {
            // return Map<Long, Author>
        });
    }

    @SchemaMapping
    public CompletableFuture<Author> author(Book book, DataLoader<Long, Author> loader) {
        return loader.load(book.getAuthorId());
    }

}

默认情况下,使用值类型的完整类名(例如 ) 的类名 ) 作为注册的键,因此只需声明 泛型类型的 method 参数提供了足够的信息 以在 .作为回退,method 参数 Resolver 还会尝试将方法参数 name 作为键,但通常不应这样做 是必要的。BatchLoaderRegistryAuthorDataLoaderDataLoaderRegistryDataLoaderspring-doc.cn

请注意,对于加载相关实体的许多情况,其中简单的 委托给 ,您可以通过使用 @BatchMapping 方法减少样板,如下一节所述。@SchemaMappingDataLoaderspring-doc.cn

5.2.8. 验证

找到 Bean 后,在带 Comments 的控制器方法上启用对 Bean 验证的支持。通常,该 bean 的类型为 。javax.validation.ValidatorAnnotatedControllerConfigurerLocalValidatorFactoryBeanspring-doc.cn

Bean 验证允许你对类型声明约束:spring-doc.cn

public class BookInput {

    @NotNull
    private String title;

    @NotNull
    @Size(max=13)
    private String isbn;
}

然后,您可以对控制器方法参数进行注释,以便在 方法调用:@Validspring-doc.cn

@Controller
public class BookController {

    @MutationMapping
    public Book addBook(@Argument @Valid BookInput bookInput) {
        // ...
    }
}

如果在验证期间发生错误,则会引发 a。 您可以使用 Exception Resolution 链来决定如何向客户端呈现该消息 将其转换为错误以包含在 GraphQL 响应中。ConstraintViolationExceptionspring-doc.cn

除了 之外,您还可以使用 Spring 的 Spring,它允许 指定验证组。@Valid@Validated

Bean 验证对于 @Argument@Arguments@ProjectedPayload 方法参数很有用,但更普遍地适用于任何方法参数。spring-doc.cn

验证和 Kotlin 协程

Hibernate Validator 与 Kotlin 协程方法不兼容,并且在 内省他们的方法参数。请参阅 spring-projects/spring-graphql#344 (评论) 以获取相关问题的链接和建议的解决方法。spring-doc.cn

5.3.@BatchMapping

批量加载通过使用 an 来延迟单个实体实例的加载,从而解决了 N+1 select 问题,因此它们 可以一起加载。例如:org.dataloader.DataLoaderspring-doc.cn

@Controller
public class BookController {

    public BookController(BatchLoaderRegistry registry) {
        registry.forTypePair(Long.class, Author.class).registerMappedBatchLoader((authorIds, env) -> {
            // return Map<Long, Author>
        });
    }

    @SchemaMapping
    public CompletableFuture<Author> author(Book book, DataLoader<Long, Author> loader) {
        return loader.load(book.getAuthorId());
    }

}

对于加载关联实体的直接情况(如上所示),该方法只不过将 .这是 样板,可以通过方法避免。例如:@SchemaMappingDataLoader@BatchMappingspring-doc.cn

@Controller
public class BookController {

    @BatchMapping
    public Mono<Map<Book, Author>> author(List<Book> books) {
        // ...
    }
}

以上内容成为 where keys 是 instances 和 loaded values their authors 中的批量加载函数。此外, a 还透明地绑定到 type 的 field ,该 只需委托给 for authors,给定其 source/parent 实例。BatchLoaderRegistryBookDataFetcherauthorBookDataLoaderBookspring-doc.cn

要用作唯一键,必须实现 和 。Bookhashcodeequalsspring-doc.cn

默认情况下,字段名称默认为方法名称,而类型名称默认为 input 元素类型的 Simple Class Name。两者都可以通过以下方式进行定制 annotation 属性。类型名称也可以从 class level 继承。List@SchemaMappingspring-doc.cn

5.3.1. 方法签名

批量映射方法支持以下参数:spring-doc.cn

方法参数 描述

List<K>spring-doc.cn

源/父对象。spring-doc.cn

java.security.Principalspring-doc.cn

从 Spring Security 上下文获取(如果可用)。spring-doc.cn

@ContextValuespring-doc.cn

要从 中访问值 , 这与 .GraphQLContextBatchLoaderEnvironmentDataFetchingEnvironmentspring-doc.cn

GraphQLContextspring-doc.cn

要从 访问上下文 , 这与 .BatchLoaderEnvironmentDataFetchingEnvironmentspring-doc.cn

BatchLoaderEnvironmentspring-doc.cn

GraphQL Java 中可用的环境到 .org.dataloader.BatchLoaderWithContextspring-doc.cn

批量映射方法可以返回:spring-doc.cn

返回类型 描述

Mono<Map<K,V>>spring-doc.cn

一个映射,其中父对象作为键,批量加载的对象作为值。spring-doc.cn

Flux<V>spring-doc.cn

批量加载的对象序列,必须与源/父对象的顺序相同 对象。spring-doc.cn

Map<K,V>,Collection<V>spring-doc.cn

命令式变体,例如,无需远程调用。spring-doc.cn

Callable<Map<K,V>>,Callable<Collection<V>>spring-doc.cn

异步调用的命令式变体。要使其正常工作,必须配置一个 .AnnotatedControllerConfigurerExecutorspring-doc.cn