6. 带注释的控制器

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

@Controller
public class GreetingController {

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

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

Spring for GraphQL 用途RuntimeWiring.Builder将上述处理程序方法注册为graphql.schema.DataFetcher对于名为“hello”的查询。spring-doc.cadn.net.cn

6.1. 声明

您可以定义@Controllerbean 作为标准 Spring bean 定义。这@Controllerstereotype 允许自动检测,与 Spring general 保持一致 支持检测@Controller@Componentclasses 和 classpath 上的 classes 和 自动注册 Bean 定义。它也充当了注释的刻板印象 类,指示其作为 GraphQL 应用程序中数据获取组件的角色。spring-doc.cadn.net.cn

AnnotatedControllerConfigurer检测@Controllerbean 并注册其 带注释的处理程序方法作为DataFetcher通过RuntimeWiring.Builder.这是一个 实现RuntimeWiringConfigurer可以添加到GraphQlSource.Builder. Spring Boot Starters自动声明AnnotatedControllerConfigurer作为豆子 并将所有RuntimeWiringConfigurerbeans 到GraphQlSource.Builder这使得 支持 CommenttedDataFetchers,请参阅 GraphQL RuntimeWiring 部分 在 Boot starter 文档中。spring-doc.cadn.net.cn

6.2.@SchemaMapping

@SchemaMapping注释将处理程序方法映射到 GraphQL 架构中的字段 并声明它是DataFetcher对于那个领域。注释可以指定 父类型名称,字段名称:spring-doc.cadn.net.cn

@Controller
public class BookController {

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

@SchemaMapping注释也可以省略这些属性,在这种情况下, 字段名称默认为方法名称,而类型名称默认为简单类 注入到方法中的源/父对象的名称。例如,以下 默认键入“书籍”和字段“author”:spring-doc.cadn.net.cn

@Controller
public class BookController {

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

@SchemaMapping可以在类级别声明注释以指定默认值 类中所有处理程序方法的类型名称。spring-doc.cadn.net.cn

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

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

}

@QueryMapping,@MutationMapping@SubscriptionMapping是元注释 本身都用@SchemaMapping并将 typeName 预设为Query,MutationSubscription分别。实际上,这些是快捷方式注释 分别用于 Query、Mutation 和 Subscription 类型下的字段。例如:spring-doc.cadn.net.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.cadn.net.cn

6.2.1. 方法签名

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

方法参数 描述

@Argumentspring-doc.cadn.net.cn

用于访问绑定到更高级别的类型化对象的命名字段参数。 看@Argument.spring-doc.cadn.net.cn

@Argumentsspring-doc.cadn.net.cn

用于访问绑定到更高级别类型 Object 的所有字段参数。 看@Arguments.spring-doc.cadn.net.cn

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

用于访问参数的原始映射,其中@Argument没有name属性。spring-doc.cadn.net.cn

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

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

@ProjectedPayload接口spring-doc.cadn.net.cn

用于通过项目接口访问字段参数。 看@ProjectedPayload接口.spring-doc.cadn.net.cn

“来源”spring-doc.cadn.net.cn

用于访问字段的源(即父/容器)实例。 参见来源spring-doc.cadn.net.cn

DataLoaderspring-doc.cadn.net.cn

要访问DataLoaderDataLoaderRegistry. 看DataLoader.spring-doc.cadn.net.cn

@ContextValuespring-doc.cadn.net.cn

用于从 mainGraphQLContextDataFetchingEnvironment.spring-doc.cadn.net.cn

@LocalContextValuespring-doc.cadn.net.cn

用于从本地访问属性GraphQLContextDataFetchingEnvironment.spring-doc.cadn.net.cn

GraphQLContextspring-doc.cadn.net.cn

要从DataFetchingEnvironment.spring-doc.cadn.net.cn

java.security.Principalspring-doc.cadn.net.cn

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

@AuthenticationPrincipalspring-doc.cadn.net.cn

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

DataFetchingFieldSelectionSetspring-doc.cadn.net.cn

要通过DataFetchingEnvironment.spring-doc.cadn.net.cn

Locale,Optional<Locale>spring-doc.cadn.net.cn

要访问LocaleDataFetchingEnvironment.spring-doc.cadn.net.cn

DataFetchingEnvironmentspring-doc.cadn.net.cn

用于直接访问基础DataFetchingEnvironment.spring-doc.cadn.net.cn

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

6.2.2.@Argument

在 GraphQL Java 中,DataFetchingEnvironment提供对特定字段地图的访问 参数值。这些值可以是简单的标量值(例如 String、Long)、Map之 值,或List的价值观。spring-doc.cadn.net.cn

使用@Argument注释,使参数绑定到目标对象,并且 注入到处理程序方法中。绑定是通过将参数值映射到 primary data 构造函数,或使用 构造函数来创建对象,然后将参数值映射到其属性。这是 递归重复,使用所有嵌套参数值并创建嵌套目标对象 因此。例如:spring-doc.cadn.net.cn

@Controller
public class BookController {

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

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

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

@Argument注释没有“必需”标志,也没有选项 指定默认值。这两者都可以在 GraphQL 架构级别指定,并且 由 GraphQL Java 强制执行。

如果绑定失败,则BindException以累积为字段的绑定问题提出 错误,其中field每个错误的参数路径是发生问题的参数路径。spring-doc.cadn.net.cn

您可以使用@Argument使用Map<String, Object>参数,以获取 所有参数值。上的 name 属性@Argument不得设置。spring-doc.cadn.net.cn

6.2.3.@Arguments

使用@Arguments注释,如果要将完整的参数映射绑定到单个 target 对象,而不是@Argument,它绑定了一个特定的命名参数。spring-doc.cadn.net.cn

例如@Argument BookInput bookInput使用参数“bookInput”的值 初始化BookInput@Arguments使用完整的参数映射,并且在 case,顶级参数绑定到BookInput性能。spring-doc.cadn.net.cn

您可以使用@Arguments使用Map<String, Object>参数,以获取 所有参数值。spring-doc.cadn.net.cn

6.2.4.@ProjectedPayload接口

作为将完整对象与@Argument, 您还可以使用投影接口通过 定义明确、最小的界面。当 Spring Data 位于类路径上时,参数投影由 Spring Data 的接口投影提供。spring-doc.cadn.net.cn

要利用这一点,请创建一个带有@ProjectedPayload并声明它作为控制器方法参数。如果参数用@Argument, 它适用于DataFetchingEnvironment.getArguments()地图。 当声明时没有@Argument,投影适用于完整参数映射中的顶级参数。spring-doc.cadn.net.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();
}

6.2.5. 源代码

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

@Controller
public class BookController {

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

source 方法参数还有助于确定映射的类型名称。如果 Java 类的简单名称与 GraphQL 类型匹配,则无需在@SchemaMapping注解。spring-doc.cadn.net.cn

一个@BatchMappinghandler 方法可以批量加载查询的所有作者,给定源/父书籍对象列表。spring-doc.cadn.net.cn

6.2.6.DataLoader

当您为实体注册批量加载函数时,如批量加载中所述,您可以访问DataLoader通过声明类型为method 类型的参数DataLoader并使用它来加载实体:spring-doc.cadn.net.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());
    }

}

默认情况下,BatchLoaderRegistry使用值类型的完整类名(例如class name 的Author) 用于注册密钥,因此只需声明 这DataLoadermethod 参数提供足够的信息以将其定位在DataLoaderRegistry. 作为后备,该DataLoader方法参数解析器也会尝试将方法参数名称作为键,但通常不应该是必要的。spring-doc.cadn.net.cn

请注意,对于加载相关实体的许多情况,其中@SchemaMapping只是 委托给DataLoader,您可以使用下一节中所述的@BatchMapping方法减少样板。spring-doc.cadn.net.cn

6.2.7. 验证

javax.validation.Validator发现豆子,AnnotatedControllerConfigurer在带注释的控制器方法上启用对 Bean 验证的支持。通常,Bean 的类型为LocalValidatorFactoryBean.spring-doc.cadn.net.cn

Bean 验证允许您声明对类型的约束:spring-doc.cadn.net.cn

public class BookInput {

    @NotNull
    private String title;

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

然后,您可以使用@Valid在之前验证它 方法调用:spring-doc.cadn.net.cn

@Controller
public class BookController {

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

如果在验证期间发生错误,则ConstraintViolationException被提高。 您可以使用异常解析链来决定如何将其呈现给客户端 将其转换为错误以包含在 GraphQL 响应中。spring-doc.cadn.net.cn

除了@Valid,也可以使用 Spring 的@Validated这允许 指定验证组。

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

验证和 Kotlin 协程

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

6.3.@BatchMapping

批量加载通过使用org.dataloader.DataLoader延迟加载单个实体实例,因此它们 可以一起加载。例如:spring-doc.cadn.net.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());
    }

}

对于加载关联实体的直接情况,如上所示,@SchemaMapping方法只不过是委托给DataLoader.这是 可以通过@BatchMapping方法。例如:spring-doc.cadn.net.cn

@Controller
public class BookController {

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

以上成为BatchLoaderRegistry键在哪里Book实例及其作者加载的值。此外,一个DataFetcher也透明地绑定到author字段Book哪 只需将委托给DataLoader对于作者,给定其来源/父级Book实例。spring-doc.cadn.net.cn

要用作唯一键,Book必须实现hashcodeequals.spring-doc.cadn.net.cn

默认情况下,字段名称默认为方法名称,而类型名称默认为 输入的简单类名List元素类型。两者都可以通过 注释属性。类型名称也可以从类级别继承@SchemaMapping.spring-doc.cadn.net.cn

6.3.1. 方法签名

批处理映射方法支持以下参数:spring-doc.cadn.net.cn

方法参数 描述

List<K>spring-doc.cadn.net.cn

源/父对象。spring-doc.cadn.net.cn

java.security.Principalspring-doc.cadn.net.cn

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

@ContextValuespring-doc.cadn.net.cn

要从GraphQLContextBatchLoaderEnvironment, 这与DataFetchingEnvironment.spring-doc.cadn.net.cn

GraphQLContextspring-doc.cadn.net.cn

要从BatchLoaderEnvironment, 这与DataFetchingEnvironment.spring-doc.cadn.net.cn

BatchLoaderEnvironmentspring-doc.cadn.net.cn

GraphQL Java 中可用的环境org.dataloader.BatchLoaderWithContext.spring-doc.cadn.net.cn

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

返回类型 描述

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

将父对象作为键,批量加载对象作为值的映射。spring-doc.cadn.net.cn

Flux<V>spring-doc.cadn.net.cn

批量加载的对象序列,必须与源/父级的顺序相同 对象传递到方法中。spring-doc.cadn.net.cn

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

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

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

要异步调用的命令式变体。为此,AnnotatedControllerConfigurer必须配置Executor.spring-doc.cadn.net.cn