跳到主要内容

服务器传输协议

DeepSeek V3 中英对照 Server Transports

Spring for GraphQL 支持通过 HTTP、WebSocket 和 RSocket 处理 GraphQL 请求的服务器端。

HTTP

GraphQlHttpHandler 用于处理基于 HTTP 的 GraphQL 请求,并将请求执行委托给拦截器链。它有两种变体:一种用于 Spring MVC,另一种用于 Spring WebFlux。两者均以异步方式处理请求,并具备相同的功能,但在写入 HTTP 响应时分别依赖于阻塞式与非阻塞式 I/O。

请求必须使用 HTTP POST 方法,内容类型为 "application/json",并将 GraphQL 请求详情作为 JSON 包含在请求体中。客户端可以请求 "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 Endpoints 部分,或者查看其中包含的 GraphQlWebMvcAutoConfigurationGraphQlWebFluxAutoConfiguration 以了解实际配置。

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

服务器发送事件

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

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

GraphQlSseHandler 的主要使用场景是作为 WebSocket 传输方式 的替代方案,用于接收作为订阅操作响应的数据流。其他类型的操作,例如查询和变更,在此处不受支持,应使用基于 HTTP 传输的普通 JSON 格式。

文件上传

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 请求。使用 WebSocket 承载 GraphQL 的主要原因是支持订阅功能,该功能允许发送 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 Endpoints 以及支持的 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 组成的链,随后由 ExecutionGraphQlService 调用 GraphQL Java 引擎。拦截器允许应用程序拦截传入的请求,以便:

  • 检查 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) {
...
}
}
  • 创建拦截器,将 HTTP 请求头值复制到 GraphQLContext 中

  • 数据控制器方法访问该值

拦截器也可以访问控制器添加到 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");
...
}
}
  • Controller 向 GraphQLContext 添加值

  • Interceptor 使用该值添加 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
});
}
}
  • 如果 ExecutionResult 的 "data" 键包含非空值,则返回相同结果

  • 检查并转换 GraphQL 错误

  • 使用修改后的错误更新 ExecutionResult

使用 WebGraphQlHandler 来配置 WebGraphQlInterceptor 链。这由 Boot Starter 支持,请参阅 Web Endpoints

WebSocketGraphQlInterceptor

WebSocketGraphQlInterceptor 继承自 WebGraphQlInterceptor,并增加了额外的回调函数来处理 WebSocket 连接的开始和结束,以及客户端取消订阅的操作。同时,它也会拦截 WebSocket 连接上的每一个 GraphQL 请求。

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

有两个内置的 WebSocket 拦截器,名为 AuthenticationWebSocketInterceptor,分别用于 WebMVC 和 WebFlux 传输方式。它们的作用是从 WebSocket 上 GraphQL 消息 "connection_init" 的有效负载中提取认证信息,进行身份验证,然后将 SecurityContext 传播到 WebSocket 连接上的后续请求。

提示

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

RSocketQlInterceptor

类似于 WebGraphQlInterceptorRSocketQlInterceptor 允许在 GraphQL Java 引擎执行前后拦截通过 RSocket 传输的 GraphQL 请求。您可以使用它来自定义 graphql.ExecutionInputgraphql.ExecutionResult