服务器传输
Spring for GraphQL 支持通过 HTTP、WebSocket 和 RSocket 的
HTTP 协议
GraphQlHttpHandler
处理 GraphQL over HTTP 请求,并委托给拦截链执行请求。有两种变体,一种用于
Spring MVC 和一个用于 Spring WebFlux 的 Spring WebFlux。两者都异步处理请求,并且具有
等效功能,但分别依赖于阻塞和非阻塞 I/O
编写 HTTP 响应。
请求必须使用 HTTP POST 和"application/json"
作为内容类型和 GraphQL 请求详细信息
作为 JSON 包含在请求正文中,如建议的 GraphQL over HTTP 规范中所定义。
成功解码 JSON 正文后,HTTP 响应状态始终为 200 (OK),
GraphQL 请求执行中的任何错误都会显示在 GraphQL 响应的“错误”部分中。
媒体类型的默认和首选选择是"application/graphql-response+json"
但"application/json"
也受支持,如规范中所述。
GraphQlHttpHandler
可以通过声明RouterFunction
bean 并使用RouterFunctions
从 Spring MVC 或 WebFlux 创建路由。Boot Starter 执行此作,请参阅 Web Endpoints 部分
details 或检查GraphQlWebMvcAutoConfiguration
或GraphQlWebFluxAutoConfiguration
它包含实际配置。
默认情况下,GraphQlHttpHandler
将使用HttpMessageConverter
(Spring MVC)
和DecoderHttpMessageReader/EncoderHttpMessageWriter
(WebFlux) 的 Web 框架。
在某些情况下,应用程序会以与 GraphQL 负载不兼容的方式为 HTTP 终端节点配置 JSON 编解码器。
应用程序可以实例化GraphQlHttpHandler
使用将用于 GraphQL 有效负载的自定义 JSON 编解码器。
此存储库的 1.0.x 分支包含一个 Spring MVC HTTP 示例应用程序。
服务器发送的事件
GraphQlSseHandler
与上面列出的 HTTP 处理程序非常相似,但这次通过 HTTP 处理 GraphQL 请求
使用 Server-Sent Events 协议。使用此传输方式,客户端必须向终端节点发送 HTTP POST 请求,其中包含"application/json"
作为内容类型和 GraphQL 请求详细信息,作为 JSON 包含在请求正文中;唯一的
与原版 HTTP 变体的区别在于,客户端必须发送"text/event-stream"
作为"Accept"
请求
页眉。响应将作为一个或多个 Server-Sent Event(服务器发送事件)发送。
这在提议的 GraphQL over HTTP 规范中也有定义。 Spring for GraphQL 仅实现“Distinct connections mode”,因此应用程序必须考虑可扩展性问题 以及采用 HTTP/2 作为底层传输是否有帮助。
主要用例GraphQlSseHandler
是 WebSocket 传输的替代方案,接收项目流作为对
订阅作。其他类型的作(如 queries 和 mutation)在此处不受支持,应支持
使用纯 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 请求。使用的主要原因
基于 WebSocket 的 GraphQL 是允许发送 GraphQL 流的订阅
responses,但它也可以用于具有单个响应的常规查询。
处理程序将每个请求委托给 Interception 链以进一步
请求执行。
基于 WebSocket 协议的 GraphQL
有两种这样的协议,一种在 subscriptions-transport-ws 库中,另一种在 graphql-ws 库中。前者不活跃且 由后者接替。阅读这篇博文了解历史。 |
有两种变体GraphQlWebSocketHandler
,一个用于 Spring MVC,一个用于
Spring WebFlux 的 Web Flux 中。两者都异步处理请求,并且具有等效的功能。
WebFlux 处理程序还使用非阻塞 I/O 和背压来流式传输消息,
这很好用,因为在 GraphQL Java 中,订阅响应是反应式流Publisher
.
这graphql-ws
project 列出了许多供客户端使用的配方。
GraphQlWebSocketHandler
可以通过声明SimpleUrlHandlerMapping
bean 并使用它将处理程序映射到 URL 路径。默认情况下,
Boot Starter 不会公开 WebSocket 端点上的 GraphQL,
但是,您可以为 Endpoint path 添加一个属性来启用它。请查看启动参考文档中的 Web 端点,以及支持的spring.graphql.websocket
属性。
您还可以查看GraphQlWebMvcAutoConfiguration
或GraphQlWebFluxAutoConfiguration
了解实际的 Boot 自动配置详细信息。
此存储库的 1.0.x 分支包含一个 WebFlux WebSocket 示例应用程序。
RSocket 系列
GraphQlRSocketHandler
处理 GraphQL over RSocket 请求。查询和更改是
expected 并作为 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
HTTP 和 WebSocket 传输调用 0 个或多个WebGraphQlInterceptor
,后跟一个ExecutionGraphQlService
调用 GraphQL Java 引擎。
拦截器允许应用程序拦截传入请求,以便:
-
检查 HTTP 请求详细信息
-
自定义
graphql.ExecutionInput
-
添加 HTTP 响应标头
-
自定义
graphql.ExecutionResult
-
以及更多
例如,拦截器可以将 HTTP 请求标头传递给DataFetcher
:
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 | Interceptor 将 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 | Controller 为GraphQLContext |
2 | 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)
});
}
}
1 | 如果返回相同的ExecutionResult 具有具有非 null 值的 “data” 键 |
2 | 检查并转换 GraphQL 错误 |
3 | 更新ExecutionResult 修改后的错误 |
用WebGraphQlHandler
要配置WebGraphQlInterceptor
链。这是支持的
通过 Boot Starter 查看 Web 端点。
WebSocketGraphQlInterceptor
WebSocketGraphQlInterceptor
延伸WebGraphQlInterceptor
使用其他回调
处理 WebSocket 连接的开始和结束,以及客户端
取消订阅。同样也会拦截
WebSocket 连接。
用WebGraphQlHandler
要配置WebGraphQlInterceptor
链。这是支持的
通过 Boot Starter 查看 Web 端点。
最多可以有一个WebSocketGraphQlInterceptor
在一连串的拦截器中。
有两个内置的 WebSocket 拦截器,称为AuthenticationWebSocketInterceptor
,
一个用于 WebMVC,一个用于 WebFlux 传输。这些有助于提取身份验证
详细信息"connection_init"
GraphQL over WebSocket 消息、身份验证、
然后传播SecurityContext
到 WebSocket 连接上的后续请求。
spring-graphql-examples中有一个websocket-authentication示例。 |
RSocketQlInterceptor
似WebGraphQlInterceptor
一RSocketQlInterceptor
允许拦截
GraphQL over RSocket 请求在 GraphQL Java 引擎执行之前和之后。您可以使用
this 自定义graphql.ExecutionInput
和graphql.ExecutionResult
.