对于最新的稳定版本,请使用Spring GraphQL 2.0.2spring-doc.cadn.net.cn

客户端

Spring for GraphQL 包括客户端支持,在HTTP、WebSocket和RSocket上执行GraphQL请求。spring-doc.cadn.net.cn

GraphQlClient

GraphQlClient 是一个契约,声明了一种独立于底层传输的 GraphQL 请求通用工作流。这意味着无论底层传输是什么,请求都使用相同的 API 执行,并且任何与传输相关的特定配置都在构建时完成。spring-doc.cadn.net.cn

要创建一个GraphQlClient,您需要以下扩展之一:spring-doc.cadn.net.cn

每个都定义了一个 Builder,其中包含与传输相关的选项。所有构建器都继承自一个通用的基础 GraphQlClient Builder,该客户端包含适用于所有扩展的选项。spring-doc.cadn.net.cn

一次获得了一个GraphQlClient后,你就可以开始进行请求了。spring-doc.cadn.net.cn

HTTP

HttpGraphQlClient 使用 WebClient 执行 GraphQL 请求。spring-doc.cadn.net.cn

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

一旦 HttpGraphQlClient 被创建,你就可以使用相同的 API 开始 执行请求,而与底层传输无关。如果你需要更改任何特定于传输的细节,可以在现有的 HttpGraphQlClient 上使用 mutate() 来创建一个新的具有自定义设置的实例: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...

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

在与HttpGraphQlClient相反的情况下,WebSocketGraphQlClient是连接导向的,这意味着它需要在发送任何请求之前建立一个连接。当你开始发送请求时,连接会透明地被建立起来。或者,可以使用客户端的start()方法,在发送任何请求之前显式地建立连接。spring-doc.cadn.net.cn

除了是连接导向的,WebSocketGraphQlClient还是多路复用的。 它维护一个单一的、共享的连接用于所有请求。如果连接丢失, 它会在下一个请求时或再次调用start()时重新建立。你也可以使用客户端的stop()方法,该方法会取消正在进行的请求,关闭 连接,并拒绝新的请求。spring-doc.cadn.net.cn

使用每个服务器上的单个WebSocketGraphQlClient实例,以便为所有对该服务器的请求共享一个连接。每个客户端实例都会建立自己的连接,这通常不是针对单个服务器的意图。

一旦 WebSocketGraphQlClient 被创建,你就可以使用相同的 API 开始 执行请求,而与底层传输无关。如果你需要更改任何特定于传输的细节,可以在现有的 WebSocketGraphQlClient 上使用 mutate() 来创建一个新的具有自定义设置的实例: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...

拦截器

GraphQL over WebSocket 协议定义了若干连接导向的消息,除了执行请求。例如,在连接开始时,客户端发送 "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

RSocket

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

在与HttpGraphQlClient相反的情况下,RSocketGraphQlClient是连接导向的,这意味着它需要在发送任何请求之前建立一个会话。当您开始发送请求时,会话将透明地建立。或者,可以使用客户端的start()方法,在发送任何请求之前明确地建立会话。spring-doc.cadn.net.cn

RSocketGraphQlClient 也被多路复用。它为所有请求维护一个单一的、共享的会话。 如果会话丢失,它会在下一次请求或再次调用 start() 时重新建立。你还可以使用客户端的 stop() 方法,该方法取消正在进行中的请求、关闭会话,并拒绝新的请求。spring-doc.cadn.net.cn

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

一旦 RSocketGraphQlClient 被创建,您就可以使用相同的 API 开始 执行请求,而不依赖于底层的传输。spring-doc.cadn.net.cn

构建器

GraphQlClient 定义一个父级 Builder,包含所有扩展构建器的共同配置选项。目前,它允许您配置:spring-doc.cadn.net.cn

请求

一旦您拥有 GraphQlClient,即可开始通过 retrieve()execute() 发起请求, 其中前者仅是后者的快捷方式。spring-doc.cadn.net.cn

检索

以下代码用于检索并解码查询数据: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 将路径上的数据解码为目标类型。

The input document is a String that could be a literal or produced through a code generated request object. You can also define documents in files and use a Document Source to resolve them by file name.spring-doc.cadn.net.cn

The path is relative to the "data" key and uses a simple dot (".") separated notation for nested fields with optional array indices for list elements, e.g. "project.name" or "project.releases[0].version".spring-doc.cadn.net.cn

解码可以返回 FieldAccessException,如果给定的路径不存在,或者字段值为 null 并且存在错误。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();
			// ...
			ClientResponseField field = ex.getField();
			// ...
		});

执行

Retrieve 只是一个从响应映射中的单个路径解码的快捷方式。如需更多控制,请使用 execute 方法并处理响应:spring-doc.cadn.net.cn

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 The response does not have data, only errors
2 字段值为null并且关联有错误
3 字段在它的DataFetcher中被设置为null
4 解码给定路径下的数据

文档源码

来自Java Spring框架文档网站的内容是一个请求,它可能定义在一个局部变量或常量中,或者通过生成的请求对象产生。spring-doc.cadn.net.cn

您也可以在类路径下的"graphql-documents/"目录下创建扩展名为.graphql.gql的文档文件,并通过文件名引用它们。spring-doc.cadn.net.cn

例如,在src/main/resources/graphql-documents目录下有一个名为projectReleases.graphql的文件,其内容为:
spring-doc.cadn.net.cn

src/main/resources/graphql-documents/projectReleases.graphql
query projectReleases($slug: ID!) {
	project(slug: $slug) {
		name
		releases {
			version
		}
	}
}

然后你可以:<br>spring-doc.cadn.net.cn

Mono<Project> projectMono = graphQlClient.documentName("projectReleases") (1)
		.variable("slug", "spring-framework") (2)
		.retrieve()
		.toEntity(Project.class);
1 从 "projectReleases.graphql" 加载文档
2 提供变量值。

"JS GraphQL"插件支持IntelliJ中的GraphQL查询文件,并提供代码完成功能。spring-doc.cadn.net.cn

您可以使用 GraphQlClient 构建器来自定义用于按名称加载文档的 DocumentSourcespring-doc.cadn.net.cn

订阅请求

GraphQlClient 可以在支持它的传输方式上执行订阅。只有 WebSocket 和 RSocket 传输方式支持 GraphQL 订阅,因此你需要创建一个 WebSocketGraphQlClientRSocketGraphQlClientspring-doc.cadn.net.cn

检索

要启动订阅流,请使用retrieveSubscription,这类似于单次响应的检索,但返回一个响应流,每个响应解码为某些数据:spring-doc.cadn.net.cn

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

The Flux 可能会终止于 SubscriptionErrorException,如果订阅从服务器端以“错误”消息结束。异常提供了访问从“错误”消息解码而来的 GraphQL 错误的功能。spring-doc.cadn.net.cn

The Flux 可能会终止于 GraphQlTransportException,例如 WebSocketDisconnectedException 如果底层连接被关闭或丢失。在这种情况下,您可以使用 retry 运算符重新启动订阅。spring-doc.cadn.net.cn

要从客户端结束订阅,必须取消 Flux,随后 WebSocket 传输层会向服务器发送一条“完成”消息。如何取消 Flux 取决于其使用方式。某些操作符(如 taketimeout)自身会取消 Flux。如果您使用 Subscriber 订阅 Flux,则可以获得对 Subscription 的引用并通过它进行取消。onSubscribe 操作符也提供了对 Subscription 的访问。spring-doc.cadn.net.cn

执行

Retrieve 只是一个快捷方式,用于从每个响应映射中的单个路径进行解码。如需更多控制,请使用 executeSubscription 方法并直接处理每个响应:spring-doc.cadn.net.cn

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

拦截

您可以通过客户端拦截所有请求创建一个GraphQlClientInterceptorspring-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();