Server Transports
Spring for GraphQL 支持 HTTP、WebSocket 和 RSocket 上的 GraphQL 请求的服务器处理。
Spring for GraphQL supports server handling of GraphQL requests over HTTP, WebSocket, and RSocket.
HTTP
GraphQlHttpHandler
处理通过 HTTP 请求的 GraphQL,并委托给 Interception 链进行请求执行。有两种变体,一种适用于 Spring MVC,一种适用于 Spring WebFlux。两者都异步处理请求,并且具有同等功能,但分别依赖于阻塞式或非阻塞式 I/O 来编写 HTTP 响应。
GraphQlHttpHandler
handles GraphQL over HTTP requests and delegates to the
Interception chain for request execution. There are two variants, one for
Spring MVC and one for Spring WebFlux. Both handle requests asynchronously and have
equivalent functionality, but rely on blocking vs non-blocking I/O respectively for
writing the HTTP response.
请求必须使用 HTTP POST,其中 "application/json"
为内容类型,并将在请求正文中以 JSON 形式包含 GraphQL 请求详细信息,如此提议的 GraphQL over HTTP 规范中所定义。一旦成功解码 JSON 正文,HTTP 响应状态始终为 200(正常),并且 GraphQL 请求执行中的任何错误都会显示在 GraphQL 响应的“错误”部分。媒体类型的默认首选是 "application/graphql-response+json"
,但 "application/json"
也受支持,如此规范中所述。
Requests must use HTTP POST with "application/json"
as content type and GraphQL request details
included as JSON in the request body, as defined in the proposed
GraphQL over HTTP specification.
Once the JSON body has been successfully decoded, the HTTP response status is always 200 (OK),
and any errors from GraphQL request execution appear in the "errors" section of the GraphQL response.
The default and preferred choice of media type is "application/graphql-response+json"
, but "application/json"
is also supported, as described in the specification.
通过声明 RouterFunction
bean 以及使用 Spring MVC 或 WebFlux 中的 RouterFunctions
来创建路由,可以将 GraphQlHttpHandler
作为一个 HTTP 终结点公开。Boot Starter 这样做,请参阅 Web 终结点 部分以获取详细信息,或检查它包含的 GraphQlWebMvcAutoConfiguration
或 GraphQlWebFluxAutoConfiguration
以获取实际配置。
GraphQlHttpHandler
can be exposed as an HTTP endpoint by declaring a RouterFunction
bean and using the RouterFunctions
from Spring MVC or WebFlux to create the route. The
Boot Starter does this, see the
Web Endpoints section for
details, or check GraphQlWebMvcAutoConfiguration
or GraphQlWebFluxAutoConfiguration
it contains, for the actual config.
此存储库的 1.0.x 分支包含一个 Spring MVC HTTP 样本 应用程序。
The 1.0.x branch of this repository contains a Spring MVC HTTP sample application.
Server-Sent Events
GraphQlSseHandler
与上面列出的 HTTP 处理器非常相似,但这次是通过 Server-Sent Events 协议处理 HTTP 上的 GraphQL 请求。通过此传输,客户端必须向使用 “application/json
” 作为内容类型并且在请求正文中包含 JSON 形式的 GraphQL 请求详细信息的端点发送 HTTP POST 请求;与传统的 HTTP 类型唯一不同的是,客户端必须向 “Accept
” 请求头发送 “text/event-stream
”。响应将作为一或多个 Server-Sent Events 发送。
GraphQlSseHandler
is very similar to the HTTP handler listed above, but this time handling GraphQL requests over HTTP
using the Server-Sent Events protocol. With this transport, clients must send HTTP POST requests to the endpoint with
"application/json"
as content type and GraphQL request details included as JSON in the request body; the only
difference with the vanilla HTTP variant is that the client must send "text/event-stream"
as the "Accept"
request
header. The response will be sent as one or more Server-Sent Event(s).
这也在建议的 GraphQL over HTTP 规范中进行了定义。Spring for GraphQL 仅实现“明确连接模式”,因此应用程序必须考虑可扩展性问题,以及采用 HTTP/2 作为底层传输是否会有帮助。
This is also defined in the proposed GraphQL over HTTP specification. Spring for GraphQL only implements the "Distinct connections mode", so applications must consider scalability concerns and whether adopting HTTP/2 as the underlying transport would help.
GraphQlSseHandler
的主要用例是 WebSocket transport 的替代方案,接收的响应是一个订阅操作的项目流。其他类型的操作(如查询和突变)在此不受支持,并且应该使用普通 JSON 通过 HTTP 传输变体。
The main use case for GraphQlSseHandler
is an alternative to the
WebSocket transport, receiving a stream of items as a response to a
subscription operation. Other types of operations, like queries and mutations, are not supported here and should be
using the plain JSON over HTTP transport variant.
File Upload
作为一种协议,GraphQL 专注于文本数据的交换。这不包括二进制数据(如图像),但有一个单独的非正式 graphql-multipart-request-spec,它允许通过 HTTP 将文件上传到 GraphQL。
As a protocol GraphQL focuses on the exchange of textual data. This doesn’t include binary data such as images, but there is a separate, informal graphql-multipart-request-spec that allows file uploads with GraphQL over HTTP.
Spring for GraphQL 并不直接支持 graphql-multipart-request-spec
。虽然规范确实提供了统一 GraphQL API 的好处,但实际经验引发了许多问题,最佳做法建议也在不断演进,请参见 Apollo Server File Upload Best Practices 进行更详细的探讨。
Spring for GraphQL does not support the graphql-multipart-request-spec
directly.
While the spec does provide the benefit of a unified GraphQL API, the actual experince has
led to a number of issues, and best practice recommendations have evolved, see
Apollo Server File Upload Best Practices
for a more detailed discussion.
如果您想在应用程序中使用 graphql-multipart-request-spec
,您可以通过库 multipart-spring-graphql 来实现。
If you would like to use graphql-multipart-request-spec
in your application, you can
do so through the library
multipart-spring-graphql.
WebSocket
GraphQlWebSocketHandler
根据 protocol 库中定义的 graphql-ws 处理通过 WebSocket 请求的 GraphQL。使用通过 WebSocket 进行 GraphQL 的主要原因是订阅,它允许发送一个 GraphQL 响应流,但也可以用于具有单个响应的常规查询。该处理程序将每个请求委托给 Interception 链,以进行进一步的请求执行。
GraphQlWebSocketHandler
handles GraphQL over WebSocket requests based on the
protocol defined in the
graphql-ws library. The main reason to use
GraphQL over WebSocket is subscriptions which allow sending a stream of GraphQL
responses, but it can also be used for regular queries with a single response.
The handler delegates every request to the Interception chain for further
request execution.
GraphQL Over WebSocket Protocols
有两种这样的协议,一个在 subscriptions-transport-ws 库中,另一个在 graphql-ws 库中。前者处于非活动状态,后者取而代之。阅读本 blog post 以了解历史。 There are two such protocols, one in the subscriptions-transport-ws library and another in the graphql-ws library. The former is not active and succeeded by the latter. Read this blog post for the history. |
GraphQlWebSocketHandler
有两个变体,一个用于 Spring MVC,另一个用于 Spring WebFlux。它们都异步处理请求并具有等效的功能。WebFlux 处理程序还使用非阻塞 I/O 和反压来流式传输消息,这很好用,因为在 GraphQL Java 中订阅响应是 Reactive Streams Publisher
。
There are two variants of GraphQlWebSocketHandler
, one for Spring MVC and one for
Spring WebFlux. Both handle requests asynchronously and have equivalent functionality.
The WebFlux handler also uses non-blocking I/O and back pressure to stream messages,
which works well since in GraphQL Java a subscription response is a Reactive Streams
Publisher
.
graphql-ws
项目列出了很多可供客户端使用的 recipes。
The graphql-ws
project lists a number of
recipes for client use.
通过声明 SimpleUrlHandlerMapping
bean 并使用它将处理程序映射到 URL 路径,可以将 GraphQlWebSocketHandler
公开为 WebSocket 终结点。默认情况下,Boot Starter 不公开通过 WebSocket 的 GraphQL 终结点,但通过添加一个用于终结点路径的属性即可轻松启用它。请参阅 Web 终结点 部分以获取详细信息,或检查 GraphQlWebMvcAutoConfiguration
或 GraphQlWebFluxAutoConfiguration
以获取实际 Boot starter 配置。
GraphQlWebSocketHandler
can be exposed as a WebSocket endpoint by declaring a
SimpleUrlHandlerMapping
bean and using it to map the handler to a URL path. By default,
the Boot Starter does not expose a GraphQL over WebSocket endpoint, but it’s easy to
enable it by adding a property for the endpoint path. Please, see the
Web Endpoints
section for details, or check the GraphQlWebMvcAutoConfiguration
or the
GraphQlWebFluxAutoConfiguration
for the actual Boot starter config.
此存储库的 1.0.x 分支包含一个 WebFlux WebSocket 样本 应用程序。
The 1.0.x branch of this repository contains a WebFlux WebSocket sample application.
RSocket
GraphQlRSocketHandler
处理 GraphQL over RSocket 请求。期待查询和突变,并将其作为 RSocket request-response
交互来处理,而订阅则作为 request-stream
来处理。
GraphQlRSocketHandler
handles GraphQL over RSocket requests. Queries and mutations are
expected and handled as an RSocket request-response
interaction while subscriptions are
handled as request-stream
.
GraphQlRSocketHandler
可以作为映射到 GraphQL 请求路由的 @Controller
中的委托使用。例如:
GraphQlRSocketHandler
can be used a delegate from an @Controller
that is mapped to
the route for GraphQL requests. For example:
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);
}
}
Interception
服务器传输允许在调用 GraphQL Java 引擎处理请求之前和之后拦截请求。
Server transports allow intercepting requests before and after the GraphQL Java engine is called to process a request.
WebGraphQlInterceptor
HTTP 和 WebSocket 传输调用一个 0 或更多 WebGraphQlInterceptor
的链,然后调用 GraphQL Java 引擎的 ExecutionGraphQlService
。WebGraphQlInterceptor
允许应用程序拦截传入请求并执行以下操作之一:
HTTP and WebSocket transports invoke a chain of
0 or more WebGraphQlInterceptor
, followed by an ExecutionGraphQlService
that calls
the GraphQL Java engine. WebGraphQlInterceptor
allows an application to intercept
incoming requests and do one of the following:
-
Check HTTP request details
-
Customize the
graphql.ExecutionInput
-
Add HTTP response headers
-
Customize the
graphql.ExecutionResult
例如,拦截器可以将 HTTP 请求头传递给 DataFetcher
:
For example, an interceptor can pass an HTTP request header to a DataFetcher
:
include-code::RequestHeaderInterceptor[]<1> 拦截器将 HTTP 请求标头值添加到 GraphQLContext<2> 数据控制器方法访问值
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 adds HTTP request header value into GraphQLContext |
2 | Data controller method accesses the value |
相反,拦截器可以访问控制器添加到 GraphQLContext
的值:
Reversely, an interceptor can access values added to the GraphQLContext
by a controller:
include-code::ResponseHeaderInterceptor[]<1> 控制器将值添加到 GraphQLContext
<2> 拦截器使用该值添加 HTTP 响应标头
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 adds value to the GraphQLContext |
2 | Interceptor uses the value to add an HTTP response header |
WebGraphQlHandler
可以修改 ExecutionResult
,例如,检查和修改在执行开始之前触发的请求验证错误,这些错误无法使用 DataFetcherExceptionResolver
处理:
WebGraphQlHandler
can modify the ExecutionResult
, for example, to inspect and modify
request validation errors that are raised before execution begins and which cannot be
handled with a DataFetcherExceptionResolver
:
include-code::RequestErrorInterceptor[]<1> 如果 ExecutionResult
具有非空值的“数据”键,则返回相同<2> 检查并转换 GraphQL 错误<3> 使用已修改的错误更新 ExecutionResult
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 | Return the same if ExecutionResult has a "data" key with non-null value |
2 | Check and transform the GraphQL errors |
3 | Update the ExecutionResult with the modified errors |
使用 WebGraphQlHandler
配置 WebGraphQlInterceptor
链。这受 Boot Starter 支持,请参阅 Web 终结点。
Use WebGraphQlHandler
to configure the WebGraphQlInterceptor
chain. This is supported
by the Boot Starter, see
Web Endpoints.
RSocketQlInterceptor
类似于 WebGraphQlInterceptor
,一个 RSocketQlInterceptor
允许在 GraphQL Java 引擎执行之前和之后拦截通过 RSocket 请求的 GraphQL。您可以使用此功能自定义 graphql.ExecutionInput
和 graphql.ExecutionResult
。
Similar to WebGraphQlInterceptor
, an RSocketQlInterceptor
allows intercepting
GraphQL over RSocket requests before and after GraphQL Java engine execution. You can use
this to customize the graphql.ExecutionInput
and the graphql.ExecutionResult
.