此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring GraphQL 1.3.4! |
客户端
Spring for GraphQL 包括对通过 HTTP 执行 GraphQL 请求的客户端支持, WebSocket 和 RSocket 的 Socket 的 Socket 和 RSocket 的 Socket 的 Socket
GraphQlClient
GraphQlClient
为 GraphQL 请求定义独立于底层的通用工作流程
transport,因此无论使用哪种 transport,您执行请求的方式都是相同的。
以下特定于 transport 的GraphQlClient
扩展可用:
每个 API 都定义了一个Builder
以及与运输相关的选项。所有构建器都扩展了
来自通用的基 GraphQlClientBuilder
具有适用于所有运输的选项。
一次GraphQlClient
构建后,您可以开始发出请求。
通常,请求的 GraphQL作以文本形式提供。或者,您
可以通过 DgsGraphQlClient 使用 DGS Codegen 客户端 API 类,它可以包装任何
以上GraphQlClient
扩展。
HTTP 同步
HttpSyncGraphQlClient
使用 RestClient 通过阻塞传输协定和
拦截 器。
RestClient restClient = RestClient.create("https://spring.io/graphql");
HttpSyncGraphQlClient graphQlClient = HttpSyncGraphQlClient.create(restClient);
一次HttpSyncGraphQlClient
创建后,您可以开始使用相同的 API 执行请求,独立于底层
运输。如果需要更改任何特定于传输的详细信息,请使用mutate()
在
现存HttpSyncGraphQlClient
要创建具有自定义设置的新实例:
RestClient restClient = RestClient.create("https://spring.io/graphql");
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 = WebClient.create("https://spring.io/graphql");
HttpGraphQlClient graphQlClient = HttpGraphQlClient.create(webClient);
一次HttpGraphQlClient
创建后,您可以开始使用相同的 API 执行请求,独立于底层
运输。如果需要更改任何特定于传输的详细信息,请使用mutate()
在
现存HttpGraphQlClient
要创建具有自定义设置的新实例:
WebClient webClient = WebClient.create("https://spring.io/graphql");
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://spring.io/graphql";
WebSocketClient client = new ReactorNettyWebSocketClient();
WebSocketGraphQlClient graphQlClient = WebSocketGraphQlClient.builder(url, client).build();
与HttpGraphQlClient
这WebSocketGraphQlClient
是面向连接的,
这意味着它需要在发出任何请求之前建立连接。开始时
要发出请求,将透明地建立连接。或者,使用
客户的start()
方法在任何请求之前显式建立连接。
除了以连接为导向之外,WebSocketGraphQlClient
也是多路复用的。
它为所有请求维护一个共享连接。如果连接丢失,
它会在下一个请求中重新建立,或者如果start()
再次调用。您还可以
使用客户端的stop()
方法取消正在进行的请求,关闭
connection 并拒绝新请求。
使用单个WebSocketGraphQlClient instance 中,以便有一个
单个共享连接,用于对该服务器的所有请求。每个客户端实例
建立自己的连接,这通常不是单个服务器的意图。 |
一次WebSocketGraphQlClient
创建后,您可以开始使用相同的 API 执行请求,独立于底层
运输。如果需要更改任何特定于传输的详细信息,请使用mutate()
在
现存WebSocketGraphQlClient
要创建具有自定义设置的新实例:
String url = "wss://spring.io/graphql";
WebSocketClient client = new ReactorNettyWebSocketClient();
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 消息以保持连接
当没有发送或接收其他消息时处于活动状态。您可以按如下方式启用它:
String url = "wss://spring.io/graphql";
WebSocketClient client = new ReactorNettyWebSocketClient();
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
}
}
将上述拦截器注册为任何其他GraphQlClientInterceptor
并使用它来拦截 GraphQL 请求,但请注意
最多可以是一个类型为WebSocketGraphQlClientInterceptor
.
RSocket 系列
RSocketGraphQlClient
使用 RSocketRequester 执行 GraphQL 请求而不是 RSocket 请求。
URI uri = URI.create("wss://localhost:8080/rsocket");
WebsocketClientTransport transport = WebsocketClientTransport.create(uri);
RSocketGraphQlClient client = RSocketGraphQlClient.builder()
.clientTransport(transport)
.build();
与HttpGraphQlClient
这RSocketGraphQlClient
是面向连接的,
这意味着它需要在发出任何请求之前建立一个会话。开始时
要发出请求,会话将以透明方式建立。或者,使用
客户的start()
方法在任何请求之前显式建立会话。
RSocketGraphQlClient
也是多路复用的。它维护一个共享会话
所有请求。如果会话丢失,则会在下一个请求中重新建立会话,或者如果start()
再次调用。您还可以使用客户端的stop()
取消
in-progress 请求,关闭会话并拒绝新请求。
使用单个RSocketGraphQlClient instance 中,以便有一个
单个共享会话,用于对该服务器的所有请求。每个客户端实例
建立自己的连接,这通常不是单个服务器的意图。 |
一次RSocketGraphQlClient
创建后,您可以开始使用相同的 API 执行请求,独立于底层
运输。
架构工人
GraphQlClient
定义父项BaseBuilder
替换为
所有扩展的构建器。目前,它允许您配置:
-
DocumentSource
从文件中加载请求的文档的策略 -
拦截已执行的请求
BaseBuilder
进一步扩展了以下内容:
-
SyncBuilder
- 使用SyncGraphQlInterceptor
的。 -
Builder
- 具有GraphQlInterceptor
的。
请求
一旦你有了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> project = graphQlClient.document(document) (1)
.retrieve("project") (2)
.toEntity(Project.class); (3)
1 | 要执行的作。 |
2 | 响应映射中要从中解码的 “data” 键下的路径。 |
3 | 解码目标类型的路径中的数据。 |
输入文档是一个String
可以是文本或通过代码生成的
generated request 对象。您还可以在文件中定义文档,并使用 Document Source 按文件名重新指定它们。
该路径是相对于 “data” 键的,并使用简单的点 (“.”) 分隔表示法
对于具有列表元素的可选数组索引的嵌套字段,例如"project.name"
或"project.releases[0].version"
.
解码可能导致FieldAccessException
如果给定路径不存在,或者
field 值为null
并出现错误。FieldAccessException
提供对
response 和字段:
-
Sync
-
Non-Blocking
try {
Project project = graphQlClient.document(document)
.retrieveSync("project")
.toEntity(Project.class);
return project;
}
catch (FieldAccessException ex) {
ClientGraphQlResponse response = ex.getResponse();
// ...
ClientResponseField field = ex.getField();
// return fallback value
return new Project();
}
Mono<Project> projectMono = graphQlClient.document(document)
.retrieve("project")
.toEntity(Project.class)
.onErrorResume(FieldAccessException.class, (ex) -> {
ClientGraphQlResponse response = ex.getResponse();
// ...
ClientResponseField field = ex.getField();
// return fallback value
return Mono.just(new Project());
});
执行
Retrieve 只是从
响应映射。如需更多控制,请使用execute
方法并处理响应:
例如:
-
Sync
-
Non-Blocking
ClientGraphQlResponse response = graphQlClient.document(document).executeSync();
if (!response.isValid()) {
// Request failure... (1)
}
ClientResponseField field = response.field("project");
if (field.getValue() == null) {
if (field.getErrors().isEmpty()) {
// Optional field set to null... (2)
}
else {
// Field failure... (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.getValue() == null) {
if (field.getErrors().isEmpty()) {
// Optional field set to null... (2)
}
else {
// Field failure... (3)
}
}
return field.toEntity(Project.class); (4)
});
1 | 响应没有数据,只有错误 |
2 | 字段,该字段设置为null 通过其DataFetcher |
3 | 字段,即null 并具有关联的错误 |
4 | 解码给定路径上的数据 |
文档源
请求的文档是一个String
,可以在局部变量中定义,或者
constant,或者它可能通过代码生成的请求对象生成。
您还可以创建带有扩展名的文档文件.graphql
或.gql
下"graphql-documents/"
并按文件名引用它们。
例如,给定一个名为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("projectReleases.project")
.toEntity(Project.class);
1 | 从 “projectReleases.graphql” 加载文档 |
2 | 提供变量值。 |
IntelliJ 的“JS GraphQL”插件支持具有代码完成的 GraphQL 查询文件。
您可以使用GraphQlClient
Builder 自定义DocumentSource
用于按名称加载文档。
订阅请求
订阅请求需要能够流式传输数据的客户端传输。
您需要创建一个GraphQlClient
支持以下内容:
取回
要启动订阅流,请使用retrieveSubscription
这类似于单个响应的 retrieve ,但返回一个
响应,每个响应都解码为一些数据:
Flux<String> greetingFlux = client.document("subscription { greetings }")
.retrieveSubscription("greeting")
.toEntity(String.class);
这Flux
可以终止于SubscriptionErrorException
如果订阅结束时间
服务器端显示 “error” 消息。异常提供对 GraphQL 错误的访问
从 “error” 消息解码。
这Flux
可以终止于GraphQlTransportException
如WebSocketDisconnectedException
如果基础连接已关闭或丢失。在那个
case 中,您可以使用retry
operator 重新启动订阅。
要从客户端结束订阅,Flux
必须取消,然后
WebSocket 传输向服务器发送 “complete” 消息。如何取消Flux
取决于它的使用方式。一些运算符(如take
或timeout
他们自己
取消Flux
.如果您订阅了Flux
替换为Subscriber
,您可以获得
对Subscription
并通过它取消。这onSubscribe
运算符
提供对Subscription
.
执行
Retrieve 只是从每个
响应映射。如需更多控制,请使用executeSubscription
方法并处理每个
直接响应:
Flux<String> greetingFlux = client.document("subscription { greetings }")
.executeSubscription()
.map((response) -> {
if (!response.isValid()) {
// Request failure...
}
ClientResponseField field = response.field("project");
if (field.getValue() == null) {
if (field.getErrors().isEmpty()) {
// Optional field set to null...
}
else {
// Field failure...
}
}
return field.toEntity(String.class);
});
拦截
对于阻止使用GraphQlClient.SyncBuilder
中,您可以创建一个SyncGraphQlClientInterceptor
要通过客户端拦截所有请求,请执行以下作:
import org.springframework.graphql.client.ClientGraphQlRequest;
import org.springframework.graphql.client.ClientGraphQlResponse;
import org.springframework.graphql.client.SyncGraphQlClientInterceptor;
public class SyncInterceptor implements SyncGraphQlClientInterceptor {
@Override
public ClientGraphQlResponse intercept(ClientGraphQlRequest request, Chain chain) {
// ...
return chain.next(request);
}
}
For non-blocking transports created with GraphQlClient.Builder
, you create a
GraphQlClientInterceptor
to intercept all requests through the client:
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.graphql.client.ClientGraphQlRequest;
import org.springframework.graphql.client.ClientGraphQlResponse;
import org.springframework.graphql.client.GraphQlClientInterceptor;
public 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);
}
}
Once the interceptor is created, register it through the client builder. For example:
URI url = URI.create("wss://localhost:8080/graphql");
WebSocketClient client = new ReactorNettyWebSocketClient();
WebSocketGraphQlClient graphQlClient = WebSocketGraphQlClient.builder(url, client)
.interceptor(new MyInterceptor())
.build();
DGS Codegen
As an alternative to providing the operation such as a mutation, query, or subscription as
text, you can use the DGS Codegen library to
generate client API classes that let you use a fluent API to define the request.
Spring for GraphQL provides DgsGraphQlClient
that wraps any GraphQlClient
and helps to prepare the request with generated client
API classes.
For example, given the following schema:
type Query {
books: [Book]
}
type Book {
id: ID
name: String
}
You can perform a request as follows:
HttpGraphQlClient client = ... ;
DgsGraphQlClient dgsClient = DgsGraphQlClient.create(client); (1)
List<Book> books = dgsClient.request(new BooksGraphQLQuery()) (2)
.projection(new BooksProjectionRoot<>().id().name()) (3)
.retrieveSync("books")
.toEntityList(Book.class);
1
- Create DgsGraphQlClient
by wrapping any GraphQlClient
.
2
- Specify the operation for the request.
3
- Define the selection set.