此版本仍在开发中,尚未被认为是稳定的。请使用最新稳定版本 Spring GraphQL 2.0.2spring-doc.cadn.net.cn

服务器传输

Spring for GraphQL 支持通过HTTP、WebSocket和RSocket处理GraphQL请求的服务器端处理。spring-doc.cadn.net.cn

HTTP

GraphQlHttpHandler 处理 GraphQL over HTTP 请求,并将请求委托给 拦截链 执行。有两个变体,一个用于 Spring MVC,另一个用于 Spring WebFlux。两者都异步处理请求并且功能等价,但分别依赖于阻塞和非阻塞 I/O 来写入 HTTP 响应。spring-doc.cadn.net.cn

请求必须使用HTTP POST方法,内容类型为"application/json",并在请求主体中包含GraphQL请求细节作为JSON。客户端可以请求"application/graphql-response+json"媒体类型以获取官方 GraphQL over HTTP规范定义的行为。如果客户端没有表达任何偏好,则此将为主要的内容类型选择。客户端还可以请求遗留的"application/json"媒体类型以获取传统的HTTP行为。spring-doc.cadn.net.cn

在实践中,如果服务器不可用、缺少安全凭据或请求体不是有效的 JSON,则GraphQL HTTP 客户端应当期望收到 4xx/5xx 状态码的 HTTP 响应。如果客户端发送的 GraphQL 文档无法解析或被 GraphQL 引擎认为无效,则"application/graphql-response+json"响应也会使用 4xx 状态码。 在这种情况下,"application/json"响应仍然会使用 200 (OK) 状态码。 一旦GraphQL 请求成功验证,HTTP 响应状态总是 200 (OK),并且 GraphQL 请求执行中的任何错误都会出现在 GraphQL 响应的“errors”部分中。spring-doc.cadn.net.cn

GraphQlHttpHandler 可以通过声明一个 RouterFunction bean 并使用 Spring MVC 或 WebFlux 的 RouterFunctions 来暴露为 HTTP 端点。Boot Starter 就会这样做,详情请参阅Web 终端点部分,或者检查 GraphQlWebMvcAutoConfigurationGraphQlWebFluxAutoConfiguration,看看其中的配置项。spring-doc.cadn.net.cn

默认情况下,GraphQlHttpHandler将使用web框架中配置的HttpMessageConverter(Spring MVC)和DecoderHttpMessageReader/EncoderHttpMessageWriter(WebFlux)来序列化和反序列化JSON数据包。 在某些情况下,应用程序可能会以与GraphQL数据包不兼容的方式为HTTP端点配置JSON编解码器。 应用程序可以实例化GraphQlHttpHandler并使用自定义的JSON编解码器来处理GraphQL数据包。spring-doc.cadn.net.cn

此仓库的 1.0.x 分支包含一个 Spring MVC HTTP 样例 应用程序。spring-doc.cadn.net.cn

服务器发送事件

GraphQlSseHandler 与上述的HTTP处理器非常相似,但这次是通过Server-Sent Events协议处理GraphQL请求。在这种传输方式中,客户端必须向端点发送HTTP POST请求,使用"application/json"作为内容类型,并在请求体中包含GraphQL请求详细信息;唯一的区别在于客户端必须发送"text/event-stream"作为"Accept"请求头。响应将以一个或多个Server-Sent Event的形式发送。spring-doc.cadn.net.cn

这在提议的 GraphQL over HTTP 规范中也有定义。 Spring for GraphQL 只实现了“独立连接模式”,因此应用程序必须考虑可扩展性问题,并考虑是否采用 HTTP/2 作为底层传输方式能有所帮助。spring-doc.cadn.net.cn

GraphQlSseHandler的主要用例是WebSocket传输的一种替代方案,用于订阅操作后接收项目流作为响应。其他类型的操作,如查询和突变,不在此处支持,并应使用纯JSON over HTTP传输的变体。spring-doc.cadn.net.cn

文件上传

作为协议,GraphQL 关注的是文本数据的交换。这不包括如图像这样的二进制数据,但有一个单独的、非正式的 graphql-multipart-request-spec 允许通过 HTTP 使用 GraphQL 进行文件上传。spring-doc.cadn.net.cn

Spring for GraphQL 不直接支持 graphql-multipart-request-spec。 虽然规范提供了统一的GraphQL API 的好处,但实际体验中出现了许多问题,最佳实践建议也随之演变,请参阅 Apollo Server 文件上传最佳实践 以获得更详细的讨论。spring-doc.cadn.net.cn

如果您希望在应用中使用graphql-multipart-request-spec,可以通过库 multipart-spring-graphql 实现。spring-doc.cadn.net.cn

WebSocket

GraphQlWebSocketHandler 基于 graphql-ws 库中定义的协议 处理 GraphQL over WebSocket 请求。使用 GraphQL over WebSocket 的主要原因在于它可以进行订阅,允许发送 GraphQL 响应流,但也可以用于常规查询并返回单个响应。处理器将每个请求委托给 拦截链 以进一步执行请求。spring-doc.cadn.net.cn

GraphQL Over WebSocket 协议

存在两个这样的协议,一个在 subscriptions-transport-ws 库中,另一个在 graphql-ws 库中。前者已经不再使用,并被后者所取代。您可以阅读这篇 博客文章 了解历史。spring-doc.cadn.net.cn

Spring 框架中有两种 GraphQlWebSocketHandler 的变体,一种用于 Spring MVC,另一种用于 Spring WebFlux。这两种方式都异步处理请求,并具有同等的功能。WebFlux 处理程序还使用非阻塞 I/O 和回压来流式传输消息,这在 GraphQL Java 中表现良好,因为订阅响应是一个 Reactive Streams Publisherspring-doc.cadn.net.cn

The graphql-ws项目列出了若干 Recipes以供客户端使用。spring-doc.cadn.net.cn

GraphQlWebSocketHandler 可以通过声明一个 SimpleUrlHandlerMapping bean 并将其用于将处理器映射到 URL 路径,从而作为 WebSocket 端点暴露。默认情况下,Boot Starter 不会暴露基于 WebSocket 的 GraphQL 端点,但您可以添加一个属性来配置端点路径以启用它。请查阅 Boot 参考文档中的 Web 端点部分,以及支持的 spring.graphql.websocket 属性列表。 您还可以查看 GraphQlWebMvcAutoConfigurationGraphQlWebFluxAutoConfiguration 以获取实际的 Boot 自动配置详情。spring-doc.cadn.net.cn

此仓库的 1.0.x 分支包含一个 WebFlux WebSocket 示例 应用程序。spring-doc.cadn.net.cn

RSocket

GraphQlRSocketHandler 处理通过 RSocket 的 GraphQL 请求。查询和突变被期望并以 RSocket request-response 交互的方式处理,而订阅则作为 request-stream 来处理。spring-doc.cadn.net.cn

GraphQlRSocketHandler 可以用作映射到GraphQL请求路由的 @Controller 的代理。例如:spring-doc.cadn.net.cn

import java.util.Map;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import org.springframework.graphql.server.GraphQlRSocketHandler;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.stereotype.Controller;

@Controller
public class GraphQlRSocketController {

	private final GraphQlRSocketHandler handler;

	GraphQlRSocketController(GraphQlRSocketHandler handler) {
		this.handler = handler;
	}

	@MessageMapping("graphql")
	public Mono<Map<String, Object>> handle(Map<String, Object> payload) {
		return this.handler.handle(payload);
	}

	@MessageMapping("graphql")
	public Flux<Map<String, Object>> handleSubscription(Map<String, Object> payload) {
		return this.handler.handleSubscription(payload);
	}
}

拦截

服务器传输允许在GraphQL Java引擎调用来处理请求之前和之后拦截请求。spring-doc.cadn.net.cn

WebGraphQlInterceptor

HTTPWebSocket 传输会调用 0 或多个 WebGraphQlInterceptor 链,随后是调用 GraphQL Java 引擎的 ExecutionGraphQlService。 拦截器允许应用程序拦截传入请求以实现以下功能:spring-doc.cadn.net.cn

例如,一个拦截器可以将HTTP请求头传递给DataFetcherspring-doc.cadn.net.cn

import java.util.Collections;

import reactor.core.publisher.Mono;

import org.springframework.graphql.data.method.annotation.ContextValue;
import org.springframework.graphql.data.method.annotation.QueryMapping;
import org.springframework.graphql.server.WebGraphQlInterceptor;
import org.springframework.graphql.server.WebGraphQlRequest;
import org.springframework.graphql.server.WebGraphQlResponse;
import org.springframework.stereotype.Controller;

class RequestHeaderInterceptor implements WebGraphQlInterceptor { (1)

	@Override
	public Mono<WebGraphQlResponse> intercept(WebGraphQlRequest request, Chain chain) {
		String value = request.getHeaders().getFirst("myHeader");
		request.configureExecutionInput((executionInput, builder) ->
				builder.graphQLContext(Collections.singletonMap("myHeader", value)).build());
		return chain.next(request);
	}
}

@Controller
class MyContextValueController { (2)

	@QueryMapping
	Person person(@ContextValue String myHeader) {
		...
	}
}
1 拦截器将HTTP请求头值添加到GraphQLContext中
2 数据控制器方法访问值

相反,一个拦截器可以访问控制器添加到GraphQLContext中的值:spring-doc.cadn.net.cn

import graphql.GraphQLContext;
import reactor.core.publisher.Mono;

import org.springframework.graphql.data.method.annotation.QueryMapping;
import org.springframework.graphql.server.WebGraphQlInterceptor;
import org.springframework.graphql.server.WebGraphQlRequest;
import org.springframework.graphql.server.WebGraphQlResponse;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseCookie;
import org.springframework.stereotype.Controller;

// Subsequent access from a WebGraphQlInterceptor

class ResponseHeaderInterceptor implements WebGraphQlInterceptor {

	@Override
	public Mono<WebGraphQlResponse> intercept(WebGraphQlRequest request, Chain chain) { (2)
		return chain.next(request).doOnNext((response) -> {
			String value = response.getExecutionInput().getGraphQLContext().get("cookieName");
			ResponseCookie cookie = ResponseCookie.from("cookieName", value).build();
			response.getResponseHeaders().add(HttpHeaders.SET_COOKIE, cookie.toString());
		});
	}
}

@Controller
class MyCookieController {

	@QueryMapping
	Person person(GraphQLContext context) { (1)
		context.put("cookieName", "123");
		...
	}
}
1 控制器向GraphQLContext添加值
2 拦截器使用该值向HTTP响应头中添加一个头部。

WebGraphQlHandler 可以修改 ExecutionResult,例如,用于检查和修改在执行开始前抛出的请求验证错误,并且这些错误无法用 DataFetcherExceptionResolver 来处理:spring-doc.cadn.net.cn

import java.util.List;

import graphql.GraphQLError;
import graphql.GraphqlErrorBuilder;
import reactor.core.publisher.Mono;

import org.springframework.graphql.server.WebGraphQlInterceptor;
import org.springframework.graphql.server.WebGraphQlRequest;
import org.springframework.graphql.server.WebGraphQlResponse;

class RequestErrorInterceptor implements WebGraphQlInterceptor {

	@Override
	public Mono<WebGraphQlResponse> intercept(WebGraphQlRequest request, Chain chain) {
		return chain.next(request).map((response) -> {
			if (response.isValid()) {
				return response; (1)
			}

			List<GraphQLError> errors = response.getErrors().stream() (2)
					.map((error) -> {
						GraphqlErrorBuilder<?> builder = GraphqlErrorBuilder.newError();
						// ...
						return builder.build();
					})
					.toList();

			return response.transform((builder) -> builder.errors(errors).build()); (3)
		});
	}
}
1 如果 `0` 具有非空的
2 检查并转换GraphQL错误
3 更新ExecutionResult为修改后的错误信息

使用WebGraphQlHandler来配置WebGraphQlInterceptor链。这由Boot Starter支持,请参见Web端点spring-doc.cadn.net.cn

WebSocketGraphQlInterceptor

WebSocketGraphQlInterceptor 扩展了 WebGraphQlInterceptor,并在此基础上添加了回调处理 WebSocket 连接的开始和结束,以及客户端取消订阅。同样地,它还会拦截 WebSocket 连接上的每一个 GraphQL 请求。spring-doc.cadn.net.cn

使用WebGraphQlHandler来配置WebGraphQlInterceptor链。这被Starters支持,参见Web端点。 在拦截器链中最多只能有一个WebSocketGraphQlInterceptorspring-doc.cadn.net.cn

有两个内置的WebSocket拦截器,分别称为AuthenticationWebSocketInterceptor,一个用于WebMVC传输,另一个用于WebFlux传输。这些帮助从GraphQL over WebSocket消息的负载中提取认证信息,进行认证,并将SecurityContext传播到WebSocket连接上的后续请求。spring-doc.cadn.net.cn

存在一个websocket-authentication 示例在 spring-graphql-examples

RSocketQlInterceptor

类似于 WebGraphQlInterceptorRSocketQlInterceptor 允许在 GraphQL Java 引擎执行前后拦截基于 RSocket 的 GraphQL 请求。您可以使用它来自定义 graphql.ExecutionInputgraphql.ExecutionResultspring-doc.cadn.net.cn