8. 客户

Spring for GraphQL 支持通过 HTTP 执行 GraphQL 请求,WebSocket 和 RSocket。spring-doc.cadn.net.cn

8.1.GraphQl客户端

GraphQl客户端是一个合同,声明了 GraphQL 请求的通用工作流程,且独立于底层传输。这意味着请求使用相同的 API 执行无论底层传输如何,且任何特定传输的配置在构建时间。spring-doc.cadn.net.cn

要创建一个GraphQl客户端你需要以下其中之一的扩展:spring-doc.cadn.net.cn

每个定义架构工人并附有与传输相关选项。所有构建者都从从一个共同的基础 GraphQlClient 扩展架构工人包含与所有扩展相关选项。spring-doc.cadn.net.cn

一旦你有了GraphQl客户端你可以开始提出请求spring-doc.cadn.net.cn

8.1.1. HTTP

HttpGraphQl客户端使用 WebClient 执行通过 HTTP 进行 GraphQL 请求。spring-doc.cadn.net.cn

WebClient webClient = ... ;
HttpGraphQlClient graphQlClient = HttpGraphQlClient.create(webClient);

一次HttpGraphQl客户端创建后,你可以开始使用相同的 API 执行请求,独立于底层 运输。 如果你需要更改任何运输具体细节,请使用变异()在一个 现存HttpGraphQl客户端创建带有自定义设置的新实例:spring-doc.cadn.net.cn

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...

8.1.2. WebSocket

WebSocketGraphQlClient通过共享的WebSocket连接执行GraphQL请求。它是用Spring WebFlux的WebSocketClient构建的,你可以按以下方式创建:spring-doc.cadn.net.cn

String url = "wss://localhost:8080/graphql";
WebSocketClient client = new ReactorNettyWebSocketClient();

WebSocketGraphQlClient graphQlClient = WebSocketGraphQlClient.builder(url, client).build();

HttpGraphQl客户端WebSocketGraphQlClient是面向连接的,这意味着它需要在发出任何请求之前建立连接。当你开始发出请求时,连接是透明建立的。或者,可以使用客户端的开始()在任何请求之前明确建立连接的方法。spring-doc.cadn.net.cn

除了注重连接,WebSocketGraphQlClient也是多路复用的。它为所有请求维护一个单一的共享连接。如果连接丢失,在下一个请求时重新建立连接,或者开始()又被叫来了。你也可以用客户的停止()该方法取消正在进行中的请求,关闭连接,并拒绝新的请求。spring-doc.cadn.net.cn

使用单人WebSocketGraphQlClient为每个服务器设置实例,以便为所有请求提供单一的共享连接。每个客户端实例建立自己的连接,这通常不是单一服务器的意图。

一次WebSocketGraphQlClient创建后,你可以开始使用相同的 API 执行请求,独立于底层 运输。 如果你需要更改任何运输具体细节,请使用变异()在一个 现存WebSocketGraphQlClient创建带有自定义设置的新实例:spring-doc.cadn.net.cn

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...
拦截 器

基于WebSocket的GraphQL协议定义了若干面向连接的消息,除了执行 请求。 例如,客户端发送“connection_init”服务器响应为“connection_ack”在一段连接的开始。spring-doc.cadn.net.cn

对于WebSocket传输专用拦截,你可以创建一个WebSocketGraphQlClientInterceptor:spring-doc.cadn.net.cn

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.spring-doc.cadn.net.cn

8.1.3. Rocket

RSocketGraphQlClient使用 RSocketRequester 在 RSocket 请求上执行 GraphQL 请求。spring-doc.cadn.net.cn

URI uri = URI.create("wss://localhost:8080/rsocket");
WebsocketClientTransport transport = WebsocketClientTransport.create(url);

RSocketGraphQlClient client = RSocketGraphQlClient.builder()
        .clientTransport(transport)
        .build();

HttpGraphQl客户端RSocketGraphQlClient是面向连接的,这意味着它需要在发出任何请求前建立会话。当你开始发送请求时,会话是透明建立的。或者,可以使用客户端的开始()在请求之前明确建立会话的方法。spring-doc.cadn.net.cn

RSocketGraphQlClient也是复用的。它维护一个单一的共享会话所有请求。如果会话丢失,会在下一个请求时重新建立,或者开始()又被叫来了。你也可以用客户的停止()该方法可抵消 进行中的请求,关闭会话并拒绝新请求。spring-doc.cadn.net.cn

使用单人RSocketGraphQlClient每个服务器的实例,以实现 对该服务器的所有请求进行单一共享会话。每个客户端实例 建立自己的连接,这通常不是单个服务器的意图。

一次RSocketGraphQlClient创建后,你可以开始使用相同的 API 执行请求,独立于底层 运输。spring-doc.cadn.net.cn

8.1.4. 建造者

GraphQl客户端定义了父母架构工人具有常见的配置选项 所有扩建项目的建造者。目前,它允许你配置:spring-doc.cadn.net.cn

8.2. 请求

一旦你有了GraphQl客户端你可以通过 retrieve()execute() 开始执行请求,前者只是后者的捷径。spring-doc.cadn.net.cn

8.2.1. 检索

以下内容检索并解码查询数据:spring-doc.cadn.net.cn

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 在目标类型路径处解码数据。

输入文档是字符串这可以是字面意义,也可以是通过代码生成的 生成请求对象。你也可以在文件中定义文档,并使用文档源按文件名重新装边。spring-doc.cadn.net.cn

路径相对于“数据”键,使用简单的点(“.”)分隔符号 对于嵌套字段,列表元素可选择数组索引,例如:“project.name”“project.releases[0].version”.spring-doc.cadn.net.cn

解码可以导致FieldAccessException如果给定路径不存在,或者 场值为并且存在一个错误。FieldAccessException提供访问 响应与现场情况:spring-doc.cadn.net.cn

Mono<Project> projectMono = graphQlClient.document(document)
        .retrieve("project")
        .toEntity(Project.class)
        .onErrorResume(FieldAccessException.class, ex -> {
            ClientGraphQlResponse response = ex.getResponse();
            // ...
            ResponseField field = ex.getField();
            // ...
        });

8.2.2. 执行

检索只是从单一路径解码的快捷方式 响应地图。为了更好地控制,可以使用执行方法和应对:spring-doc.cadn.net.cn

Mono<Project> projectMono = graphQlClient.document(document)
        .execute()
        .map(response -> {
            if (!response.isValid()) {
                // Request failure... (1)
            }

            ResponseField 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 即 场并且存在一个相关的误差
3 该字段被设置为由其数据费彻
4 解码给定路径上的数据

8.2.3. 文档来源

请求的文件是字符串可以定义在局部变量中,或者 常数,或者通过代码生成的请求对象生成。spring-doc.cadn.net.cn

你也可以创建带有扩展名的文档文件.graphql.gql“GraphQL-文档/”在类路径上,并用文件名来引用它们。spring-doc.cadn.net.cn

例如,给定一个名为projectReleases.graphqlsrc/main/resources/graphql-documents,内容如下:spring-doc.cadn.net.cn

src/main/resources/graphql-documents/projectReleases.graphql
query projectReleases($slug: ID!) {
    project(slug: $slug) {
        name
        releases {
            version
        }
    }
}
Mono<Project> projectMono = graphQlClient.documentName("projectReleases") (1)
        .variable("slug", "spring-framework") (2)
        .retrieve()
        .toEntity(Project.class);
1 从“projectReleases.graphql”加载文档
2 提供变量值。

IntelliJ 的“JS GraphQL”插件支持带代码补全的 GraphQL 查询文件。spring-doc.cadn.net.cn

你可以使用GraphQl客户端 构建者可定制文档来源用于按姓名加载文件。spring-doc.cadn.net.cn

8.3. 订阅请求

GraphQl客户端可以通过支持它的传输执行订阅。目前,只有 WebSocket 传输支持 GraphQL 流,所以你需要创建一个 WebSocketGraphQlClientspring-doc.cadn.net.cn

8.3.1. 取回

要开始订阅流,请使用获取订阅这类似于对单次响应的检索,但返回的是 每个响应都被解码为某些数据:spring-doc.cadn.net.cn

Flux<String> greetingFlux = client.document("subscription { greetings }")
        .retrieveSubscription("greeting")
        .toEntity(String.class);

订阅直播可能以以下内容结束:spring-doc.cadn.net.cn

  • 订阅错误异常如果服务器结束 订阅时包含一个或多个GraphQL错误的明确“错误”消息。 该例外允许访问该消息中解码的 GraphQL 错误。spring-doc.cadn.net.cn

  • GraphQlTransportExceptionWebSocketDisconnectedException如果 连接断开或断开,这种情况下你可以使用重试运营商将重新建立 连接并重新开始订阅。spring-doc.cadn.net.cn

8.3.2. 执行

Retrieve只是从每个路径中解码的快捷方式 响应地图。为了更好地控制,可以使用执行订阅方法和处理每个 直接回复:spring-doc.cadn.net.cn

Flux<String> greetingFlux = client.document("subscription { greetings }")
        .executeSubscription()
        .map(response -> {
            if (!response.isValid()) {
                // Request failure...
            }

            ResponseField 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)
        });

8.4. 拦截

你创建了一个GraphQlClientInterceptor通过客户端拦截所有请求:spring-doc.cadn.net.cn

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);
    }

}

截获器创建后,通过客户端构建器注册:spring-doc.cadn.net.cn

URI url = ... ;
WebSocketClient client = ... ;

WebSocketGraphQlClient graphQlClient = WebSocketGraphQlClient.builder(url, client)
        .interceptor(new MyInterceptor())
        .build();