Spring for GraphQL 包括对通过 HTTP 执行 GraphQL 请求的客户端支持, WebSocket 和 RSocket 的 Socket 的 Socket 和 RSocket 的 Socket 的 Socket
GraphQlClient
GraphQlClient
为 GraphQL 请求定义独立于底层的通用工作流程
transport,因此无论使用哪种 transport,您执行请求的方式都是相同的。
可以使用以下特定于传输的扩展:GraphQlClient
每个 define a 都包含与传输相关的选项。所有构建器都扩展了
来自通用的基本 GraphQlClient Builder
,具有适用于所有传输的选项。Builder
构建完成后,您就可以开始提出请求了。GraphQlClient
通常,请求的 GraphQL 操作以文本形式提供。或者,您
可以通过 DgsGraphQlClient 使用 DGS Codegen 客户端 API 类,它可以包装任何
。GraphQlClient
HTTP 同步
HttpSyncGraphQlClient
使用 RestClient 通过阻塞传输协定和
拦截 器。
RestClient restClient = ... ;
HttpSyncGraphQlClient graphQlClient = HttpSyncGraphQlClient.create(restClient);
创建后,您可以开始使用相同的 API 执行请求,独立于底层
运输。如果您需要更改任何特定于传输的详细信息,请在
existing 创建具有自定义设置的新实例:HttpSyncGraphQlClient
mutate()
HttpSyncGraphQlClient
RestClient restClient = ... ;
HttpSyncGraphQlClient graphQlClient = HttpSyncGraphQlClient.builder(restClient)
.headers(headers -> headers.setBasicAuth("joe", "..."))
.build();
// Perform requests with graphQlClient...
HttpSyncGraphQlClient anotherGraphQlClient = graphQlClient.mutate()
.headers(headers -> headers.setBasicAuth("peter", "..."))
.build();
// Perform requests with anotherGraphQlClient...
HTTP 协议
HttpGraphQlClient
使用 WebClient 执行
通过非阻塞传输协定和
拦截 器。
WebClient webClient = ... ;
HttpGraphQlClient graphQlClient = HttpGraphQlClient.create(webClient);
创建后,您可以开始使用相同的 API 执行请求,独立于底层
运输。如果您需要更改任何特定于传输的详细信息,请在
existing 创建具有自定义设置的新实例:HttpGraphQlClient
mutate()
HttpGraphQlClient
WebClient webClient = ... ;
HttpGraphQlClient graphQlClient = HttpGraphQlClient.builder(webClient)
.headers(headers -> headers.setBasicAuth("joe", "..."))
.build();
// Perform requests with graphQlClient...
HttpGraphQlClient anotherGraphQlClient = graphQlClient.mutate()
.headers(headers -> headers.setBasicAuth("peter", "..."))
.build();
// Perform requests with anotherGraphQlClient...
WebSocket 浏览器
WebSocketGraphQlClient
通过共享 WebSocket 连接执行 GraphQL 请求。
它是使用 Spring WebFlux 的 WebSocketClient 构建的,你可以按如下方式创建它:
String url = "wss://localhost:8080/graphql";
WebSocketClient client = new ReactorNettyWebSocketClient();
WebSocketGraphQlClient graphQlClient = WebSocketGraphQlClient.builder(url, client).build();
与 相比,它是面向连接的,
这意味着它需要在发出任何请求之前建立连接。开始时
要发出请求,将透明地建立连接。或者,使用
client's 方法在任何请求之前显式建立连接。HttpGraphQlClient
WebSocketGraphQlClient
start()
除了面向连接之外,还进行了多路复用。
它为所有请求维护一个共享连接。如果连接丢失,
它会在下一个请求或 if 再次调用时重新建立。您还可以
使用客户端的方法取消正在进行的请求,关闭
connection 并拒绝新请求。WebSocketGraphQlClient
start()
stop()
为每个服务器使用单个实例,以便获得
单个共享连接,用于对该服务器的所有请求。每个客户端实例
建立自己的连接,这通常不是单个服务器的意图。WebSocketGraphQlClient |
创建后,您可以开始使用相同的 API 执行请求,独立于底层
运输。如果您需要更改任何特定于传输的详细信息,请在
existing 创建具有自定义设置的新实例:WebSocketGraphQlClient
mutate()
WebSocketGraphQlClient
URI url = ... ;
WebSocketClient client = ... ;
WebSocketGraphQlClient graphQlClient = WebSocketGraphQlClient.builder(url, client)
.headers(headers -> headers.setBasicAuth("joe", "..."))
.build();
// Use graphQlClient...
WebSocketGraphQlClient anotherGraphQlClient = graphQlClient.mutate()
.headers(headers -> headers.setBasicAuth("peter", "..."))
.build();
// Use anotherGraphQlClient...
WebSocketGraphQlClient
支持发送定期 ping 消息以保持连接
当没有发送或接收其他消息时处于活动状态。您可以按如下方式启用它:
URI url = ... ;
WebSocketClient client = ... ;
WebSocketGraphQlClient graphQlClient = WebSocketGraphQlClient.builder(url, client)
.keepAlive(Duration.ofSeconds(30))
.build();
拦截 器
GraphQL over WebSocket 协议除了定义许多面向连接的消息外,还执行
请求。例如,客户端发送,服务器在连接开始时响应。"connection_init"
"connection_ack"
对于特定于 WebSocket 传输的拦截,您可以创建一个:WebSocketGraphQlClientInterceptor
static class MyInterceptor implements WebSocketGraphQlClientInterceptor {
@Override
public Mono<Object> connectionInitPayload() {
// ... the "connection_init" payload to send
}
@Override
public Mono<Void> handleConnectionAck(Map<String, Object> ackPayload) {
// ... the "connection_ack" payload received
}
}
将上述拦截器注册为任何其他拦截器,并使用它来拦截 GraphQL 请求,但请注意
最多可以是一个类型为 的拦截器 。GraphQlClientInterceptor
WebSocketGraphQlClientInterceptor
RSocket 系列
RSocketGraphQlClient
使用 RSocketRequester 执行 GraphQL 请求而不是 RSocket 请求。
URI uri = URI.create("wss://localhost:8080/rsocket");
WebsocketClientTransport transport = WebsocketClientTransport.create(url);
RSocketGraphQlClient client = RSocketGraphQlClient.builder()
.clientTransport(transport)
.build();
与 相比,它是面向连接的,
这意味着它需要在发出任何请求之前建立一个会话。开始时
要发出请求,会话将以透明方式建立。或者,使用
client's 方法显式建立会话。HttpGraphQlClient
RSocketGraphQlClient
start()
RSocketGraphQlClient
也是多路复用的。它维护一个共享会话
所有请求。如果会话丢失,则会在下一个请求或再次调用时重新建立会话。您还可以使用 client's 方法,该方法将
in-progress 请求,关闭会话并拒绝新请求。start()
stop()
为每个服务器使用单个实例,以便获得
单个共享会话,用于对该服务器的所有请求。每个客户端实例
建立自己的连接,这通常不是单个服务器的意图。RSocketGraphQlClient |
创建后,您可以开始使用相同的 API 执行请求,独立于底层
运输。RSocketGraphQlClient
架构工人
GraphQlClient
定义具有
所有扩展的构建器。目前,它允许您配置:BaseBuilder
-
DocumentSource
从文件中加载请求的文档的策略 -
拦截已执行的请求
BaseBuilder
进一步扩展了以下内容:
-
SyncBuilder
- 使用 's 链阻塞执行堆栈。SyncGraphQlInterceptor
-
Builder
- 具有 's 链的非阻塞执行堆栈。GraphQlInterceptor
为每个服务器使用单个实例,以便获得
单个共享连接,用于对该服务器的所有请求。每个客户端实例
建立自己的连接,这通常不是单个服务器的意图。WebSocketGraphQlClient |
为每个服务器使用单个实例,以便获得
单个共享会话,用于对该服务器的所有请求。每个客户端实例
建立自己的连接,这通常不是单个服务器的意图。RSocketGraphQlClient |
请求
拥有 GraphQlClient
后,您可以开始通过 retrieve 或 execute 方法执行请求。
取回
下面检索和解码查询的数据:
-
Sync
-
Non-Blocking
String document = "{" +
" project(slug:\"spring-framework\") {" +
" name" +
" releases {" +
" version" +
" }"+
" }" +
"}";
Project project = graphQlClient.document(document) (1)
.retrieveSync("project") (2)
.toEntity(Project.class); (3)
String document = "{" +
" project(slug:\"spring-framework\") {" +
" name" +
" releases {" +
" version" +
" }"+
" }" +
"}";
Mono<Project> projectMono = graphQlClient.document(document) (1)
.retrieve("project") (2)
.toEntity(Project.class); (3)
1 | 要执行的操作。 |
2 | 响应映射中要从中解码的 “data” 键下的路径。 |
3 | 解码目标类型的路径中的数据。 |
输入文档可以是文本或通过代码生成的
generated request 对象。您还可以在文件中定义文档,并使用 Document Source 按文件名重新指定它们。String
该路径是相对于 “data” 键的,并使用简单的点 (“.”) 分隔表示法
对于具有列表元素的可选数组索引的嵌套字段,例如 或。"project.name"
"project.releases[0].version"
如果给定路径不存在,或者
field 值为 和 有错误。 提供对
response 和字段:FieldAccessException
null
FieldAccessException
-
Sync
-
Non-Blocking
try {
Project project = graphQlClient.document(document)
.retrieveSync("project")
.toEntity(Project.class);
}
catch (FieldAccessException ex) {
ClientGraphQlResponse response = ex.getResponse();
// ...
ClientResponseField field = ex.getField();
// ...
}
Mono<Project> projectMono = graphQlClient.document(document)
.retrieve("project")
.toEntity(Project.class)
.onErrorResume(FieldAccessException.class, ex -> {
ClientGraphQlResponse response = ex.getResponse();
// ...
ClientResponseField field = ex.getField();
// ...
});
执行
Retrieve 只是从
响应映射。如需更多控制,请使用该方法并处理响应:execute
例如:
-
Sync
-
Non-Blocking
ClientGraphQlResponse response = graphQlClient.document(document).executeSync();
if (!response.isValid()) {
// Request failure... (1)
}
ClientResponseField field = response.field("project");
if (!field.hasValue()) {
if (field.getError() != null) {
// Field failure... (2)
}
else {
// Optional field set to null... (3)
}
}
Project project = field.toEntity(Project.class); (4)
Mono<Project> projectMono = graphQlClient.document(document)
.execute()
.map(response -> {
if (!response.isValid()) {
// Request failure... (1)
}
ClientResponseField field = response.field("project");
if (!field.hasValue()) {
if (field.getError() != null) {
// Field failure... (2)
}
else {
// Optional field set to null... (3)
}
}
return field.toEntity(Project.class); (4)
});
1 | 响应没有数据,只有错误 |
2 | 字段是 和 具有关联错误null |
3 | 字段,该字段由其null DataFetcher |
4 | 解码给定路径上的数据 |
文档源
请求的文档是可以在局部变量中定义的 a,或者
constant,或者它可能通过代码生成的请求对象生成。String
您还可以在类路径中创建带有扩展名或 under 的文档文件,并通过文件名引用它们。.graphql
.gql
"graphql-documents/"
例如,给定一个名为 in 的文件,其中包含内容:projectReleases.graphql
src/main/resources/graphql-documents
query projectReleases($slug: ID!) {
project(slug: $slug) {
name
releases {
version
}
}
}
然后,您可以:
Project project = graphQlClient.documentName("projectReleases") (1)
.variable("slug", "spring-framework") (2)
.retrieveSync()
.toEntity(Project.class);
1 | 从 “projectReleases.graphql” 加载文档 |
2 | 提供变量值。 |
此方法也适用于为查询加载片段。
片段是可重用的字段选择集,可避免在请求文档中重复。
例如,我们可以在多个查询中使用一个 fragment:…releases
query frameworkReleases {
project(slug: "spring-framework") {
name
...releases
}
}
query graphqlReleases {
project(slug: "spring-graphql") {
name
...releases
}
}
此片段可以在单独的文件中定义以供重用:
fragment releases on Project {
releases {
version
}
}
然后,您可以沿查询文档发送此片段:
Project project = graphQlClient.documentName("projectReleases") (1)
.fragmentName("releases") (2)
.retrieveSync()
.toEntity(Project.class);
1 | 从 “projectReleases.graphql” 加载文档 |
2 | 从 “releases.graphql” 加载片段并将其附加到文档中 |
IntelliJ 的“JS GraphQL”插件支持具有代码完成的 GraphQL 查询文件。
您可以使用 Builder 自定义 以按名称加载文档。GraphQlClient
DocumentSource
1 | 要执行的操作。 |
2 | 响应映射中要从中解码的 “data” 键下的路径。 |
3 | 解码目标类型的路径中的数据。 |
1 | 响应没有数据,只有错误 |
2 | 字段是 和 具有关联错误null |
3 | 字段,该字段由其null DataFetcher |
4 | 解码给定路径上的数据 |
1 | 从 “projectReleases.graphql” 加载文档 |
2 | 提供变量值。 |
1 | 从 “projectReleases.graphql” 加载文档 |
2 | 从 “releases.graphql” 加载片段并将其附加到文档中 |
订阅请求
订阅请求需要能够流式传输数据的客户端传输。
您需要创建一个支持此功能的 API:GraphQlClient
取回
要启动订阅流,请使用 which 类似于 retrieve 来获取单个响应,但返回
响应,每个响应都解码为一些数据:retrieveSubscription
Flux<String> greetingFlux = client.document("subscription { greetings }")
.retrieveSubscription("greeting")
.toEntity(String.class);
如果订阅从
服务器端显示 “error” 消息。异常提供对 GraphQL 错误的访问
从 “error” 消息解码。Flux
SubscriptionErrorException
如果底层连接已关闭或丢失,则可能会终止。在那个
的情况下,您可以使用 Operator 重新启动订阅。Flux
GraphQlTransportException
WebSocketDisconnectedException
retry
要从客户端结束订阅,必须取消 ,然后
WebSocket 传输向服务器发送 “complete” 消息。如何取消 取决于它的使用方式。一些运算符,例如 or 自己
取消 .如果您使用 订阅 ,则可以获得
引用 and cancel 通过它。操作员还
提供对 的访问。Flux
Flux
take
timeout
Flux
Flux
Subscriber
Subscription
onSubscribe
Subscription
执行
Retrieve 只是从每个
响应映射。要获得更多控制,请使用 method 并处理每个
直接响应:executeSubscription
Flux<String> greetingFlux = client.document("subscription { greetings }")
.executeSubscription()
.map(response -> {
if (!response.isValid()) {
// Request failure...
}
ClientResponseField field = response.field("project");
if (!field.hasValue()) {
if (field.getError() != null) {
// Field failure...
}
else {
// Optional field set to null... (3)
}
}
return field.toEntity(String.class)
});
拦截
对于使用 创建的阻止传输,您可以创建一个以拦截通过客户端的所有请求:GraphQlClient.SyncBuilder
SyncGraphQlClientInterceptor
static class MyInterceptor implements SyncGraphQlClientInterceptor {
@Override
public ClientGraphQlResponse intercept(ClientGraphQlRequest request, Chain chain) {
// ...
return chain.next(request);
}
}
对于使用 创建的非阻塞传输,您可以创建一个以通过客户端拦截所有请求:GraphQlClient.Builder
GraphQlClientInterceptor
static class MyInterceptor implements GraphQlClientInterceptor {
@Override
public Mono<ClientGraphQlResponse> intercept(ClientGraphQlRequest request, Chain chain) {
// ...
return chain.next(request);
}
@Override
public Flux<ClientGraphQlResponse> interceptSubscription(ClientGraphQlRequest request, SubscriptionChain chain) {
// ...
return chain.next(request);
}
}
创建侦听器后,通过 Client 端构建器注册它。例如:
URI url = ... ;
WebSocketClient client = ... ;
WebSocketGraphQlClient graphQlClient = WebSocketGraphQlClient.builder(url, client)
.interceptor(new MyInterceptor())
.build();
DGS 代码生成
作为将 mutation、query 或 subscription 等操作作为 文本中,您可以使用 DGS Codegen 库 生成客户端 API 类,让您使用 Fluent API 来定义请求。
Spring for GraphQL 提供了 DgsGraphQlClient,它包装了任何 DgsGraphQlClient 并帮助使用生成的客户端准备请求
API 类。GraphQlClient
例如,给定以下架构:
type Query {
books: [Book]
}
type Book {
id: ID
name: String
}
您可以按如下方式执行请求:
HttpGraphQlClient client = ... ;
DgsGraphQlClient dgsClient = DgsGraphQlClient.create(client); (1)
List<Book> books = dgsClient.request(new BooksGraphQLQuery()) (2)
.projection(new BooksProjectionRoot<>().id().name()) (3)
.retrieveSync()
.toEntityList(Book.class);
1 | - 通过包装任何 .DgsGraphQlClient GraphQlClient |
2 | - 指定请求的操作。 |
3 | - 定义选择集。 |
1 | - 通过包装任何 .DgsGraphQlClient GraphQlClient |
2 | - 指定请求的操作。 |
3 | - 定义选择集。 |