服务器传输

Spring for GraphQL 支持通过 HTTP、WebSocket 和 RSocket 处理 GraphQL 请求。

HTTP

GraphQlHttpHandler 处理通过 HTTP 的 GraphQL 请求,并委托给 拦截 链执行请求。有两种变体,一种用于 Spring MVC,另一种用于 Spring WebFlux。两者都异步处理请求,并具有等效的功能,但分别依赖于阻塞或非阻塞 I/O 来写入 HTTP 响应。

请求必须使用 HTTP POST,内容类型为 "application/json",并在请求正文中以 JSON 形式包含 GraphQL 请求详细信息。客户端可以请求 "application/graphql-response+json" 媒体类型以获取官方 GraphQL over HTTP 规范中定义的行为。如果客户端未明确表达任何偏好,这将是首选内容类型。客户端也可以请求传统的 "application/json" 媒体类型以获取传统的 HTTP 行为。

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

GraphQlHttpHandler 可以通过声明一个 RouterFunction bean 并使用 Spring MVC 或 WebFlux 的 RouterFunctions 创建路由来作为 HTTP 端点暴露。 Boot Starter 就实现了这一点,详细信息请参阅 Web 端点 部分,或者查看它包含的 GraphQlWebMvcAutoConfigurationGraphQlWebFluxAutoConfiguration 以获取实际配置。

默认情况下,GraphQlHttpHandler 将使用 Web 框架中配置的 HttpMessageConverter (Spring MVC) 和 DecoderHttpMessageReader/EncoderHttpMessageWriter (WebFlux) 来序列化和反序列化 JSON 负载。在某些情况下,应用程序将以与 GraphQL 负载不兼容的方式配置 HTTP 端点的 JSON 编解码器。应用程序可以使用自定义 JSON 编解码器实例化 GraphQlHttpHandler,该编解码器将用于 GraphQL 负载。

服务器发送事件 (Server-Sent Events)

GraphQlSseHandler 与上面列出的 HTTP 处理程序非常相似,但这次使用服务器发送事件协议处理通过 HTTP 的 GraphQL 请求。使用此传输方式,客户端必须向端点发送 HTTP POST 请求,内容类型为 "application/json",并在请求正文中以 JSON 形式包含 GraphQL 请求详细信息;与普通 HTTP 变体的唯一区别是客户端必须发送 "text/event-stream" 作为 "Accept" 请求头。响应将以一个或多个服务器发送事件的形式发送。

这也在拟议的 GraphQL over HTTP 规范中定义。Spring for GraphQL 只实现了“独立连接模式”,因此应用程序必须考虑可伸缩性问题以及采用 HTTP/2 作为底层传输是否有帮助。

GraphQlSseHandler 的主要用例是作为 WebSocket 传输 的替代方案,用于接收作为订阅操作响应的项目流。此处不支持其他类型的操作,如查询和变更,这些操作应使用纯 JSON over HTTP 传输变体。

文件上传

作为一种协议,GraphQL 专注于文本数据的交换。这不包括图像等二进制数据,但有一个单独的非正式 graphql-multipart-request-spec 允许通过 HTTP 上传文件到 GraphQL。

Spring for GraphQL 不直接支持 graphql-multipart-request-spec。虽然该规范确实提供了统一 GraphQL API 的好处,但实际经验导致了一些问题,最佳实践建议也随之演变,请参阅 Apollo Server 文件上传最佳实践 以获取更详细的讨论。

如果您想在应用程序中使用 graphql-multipart-request-spec,可以通过 multipart-spring-graphql 库实现。

WebSocket

GraphQlWebSocketHandler 根据 graphql-ws 库中定义的 协议 处理通过 WebSocket 的 GraphQL 请求。使用 GraphQL over WebSocket 的主要原因是订阅,它允许发送 GraphQL 响应流,但它也可以用于具有单个响应的常规查询。处理程序将每个请求委托给 拦截 链以进一步执行请求。

GraphQL Over WebSocket 协议

存在两种这样的协议,一种在 subscriptions-transport-ws 库中,另一种在 graphql-ws 库中。前者不再活跃,已被后者取代。请阅读此 博客文章 了解其历史。

GraphQlWebSocketHandler 有两种变体,一种用于 Spring MVC,另一种用于 Spring WebFlux。两者都异步处理请求并具有等效功能。WebFlux 处理程序还使用非阻塞 I/O 和背压来流传输消息,这在 GraphQL Java 中订阅响应是 Reactive Streams Publisher 时非常有效。

graphql-ws 项目列出了许多供客户端使用的 “食谱”

GraphQlWebSocketHandler 可以通过声明一个 SimpleUrlHandlerMapping bean 并使用它将处理程序映射到 URL 路径来作为 WebSocket 端点公开。默认情况下, Boot Starter 不会公开 GraphQL over WebSocket 端点,但您可以为端点路径添加属性以启用它。请查阅 Boot 参考文档中的 Web 端点,以及支持的 spring.graphql.websocket 属性 列表。您还可以查看 GraphQlWebMvcAutoConfigurationGraphQlWebFluxAutoConfiguration 以获取实际的 Boot 自动配置详细信息。

此存储库的 1.0.x 分支包含一个 WebFlux WebSocket 示例 应用程序。

RSocket

GraphQlRSocketHandler 处理通过 RSocket 的 GraphQL 请求。查询和变更被预期并作为 RSocket request-response 交互处理,而订阅则作为 request-stream 处理。

GraphQlRSocketHandler 可以作为 @Controller 的委托,映射到 GraphQL 请求的路由。例如:

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 引擎处理请求之前和之后拦截请求。

WebGraphQlInterceptor

HTTPWebSocket 传输会调用一个包含 0 个或多个 WebGraphQlInterceptor 的链,然后是调用 GraphQL Java 引擎的 ExecutionGraphQlService。拦截器允许应用程序拦截传入请求,以便

  • 检查 HTTP 请求详细信息

  • 定制 graphql.ExecutionInput

  • 添加 HTTP 响应头

  • 定制 graphql.ExecutionResult

  • 等等

Spring for GraphQL 提供了一个内置的 HttpRequestHeaderInterceptor,它将 HTTP 头从请求复制到 GraphQL 上下文,然后使其可供数据获取器(例如带注解的控制器)使用。例如,在 Spring Boot 应用程序中,这可以通过以下方式完成:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.graphql.data.method.annotation.ContextValue;
import org.springframework.graphql.data.method.annotation.QueryMapping;
import org.springframework.graphql.server.support.HttpRequestHeaderInterceptor;
import org.springframework.stereotype.Controller;

@Configuration
class RequestHeaderInterceptorConfig {

	@Bean
	public HttpRequestHeaderInterceptor headerInterceptor() { (1)
		return HttpRequestHeaderInterceptor.builder().mapHeader("myHeader").build();
	}
}

@Controller
class MyContextValueController { (2)

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

拦截器还可以访问控制器添加到 GraphQLContext 中的值

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 处理。

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 如果 ExecutionResult 具有非空值的“data”键,则返回相同的值
2 检查并转换 GraphQL 错误
3 使用修改后的错误更新 ExecutionResult

使用 WebGraphQlHandler 配置 WebGraphQlInterceptor 链。 Boot Starter 支持此功能,请参阅 Web 端点

WebSocketGraphQlInterceptor

WebSocketGraphQlInterceptor 扩展了 WebGraphQlInterceptor,增加了额外的回调来处理 WebSocket 连接的开始和结束,以及客户端取消订阅。它还拦截 WebSocket 连接上的每个 GraphQL 请求。

使用 WebGraphQlHandler 配置 WebGraphQlInterceptor 链。 Boot Starter 支持此功能,请参阅 Web 端点。在拦截器链中最多只能有一个 WebSocketGraphQlInterceptor

有两个内置的 WebSocket 拦截器,名为 AuthenticationWebSocketInterceptor,一个用于 WebMVC,另一个用于 WebFlux 传输。它们有助于从 GraphQL over WebSocket 消息的 "connection_init" 有效载荷中提取身份验证详细信息,进行身份验证,然后将 SecurityContext 传播到 WebSocket 连接上的后续请求。

spring-graphql-examples 中有一个 websocket-authentication 示例。

RSocketQlInterceptor

类似于 WebGraphQlInterceptorRSocketQlInterceptor 允许在 GraphQL Java 引擎执行之前和之后拦截 GraphQL over RSocket 请求。您可以使用此功能自定义 graphql.ExecutionInputgraphql.ExecutionResult

© . This site is unofficial and not affiliated with VMware.