2. 服务器传输

Spring for GraphQL 支持通过 HTTP、WebSocket 和 RSocket 的spring-doc.cn

2.1. HTTP协议

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

请求必须使用 HTTP POST 作为内容类型和 GraphQL 请求详细信息 作为 JSON 包含在请求正文中,如建议的 GraphQL over HTTP 规范中所定义。 成功解码 JSON 正文后,HTTP 响应状态始终为 200 (OK), GraphQL 请求执行中的任何错误都会显示在 GraphQL 响应的“错误”部分中。 媒体类型的默认和首选选择是 ,但也受支持,如规范中所述。"application/json""application/graphql-response+json""application/json"spring-doc.cn

GraphQlHttpHandler可以通过声明 bean 并使用 from Spring MVC 或 WebFlux 创建路由来公开为 HTTP 端点。Boot Starter 执行此操作,请参阅 Web Endpoints 部分 details 或选中 or it contains,以获取实际配置。RouterFunctionRouterFunctionsGraphQlWebMvcAutoConfigurationGraphQlWebFluxAutoConfigurationspring-doc.cn

此存储库的 1.0.x 分支包含一个 Spring MVC HTTP 示例应用程序。spring-doc.cn

2.1.1. 文件上传

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

Spring for GraphQL 不支持直接。 虽然该规范确实提供了统一 GraphQL API 的好处,但实际体验确实 导致了许多问题,最佳实践建议已经发展起来,请参阅 Apollo Server 文件上传最佳实践 有关更详细的讨论。graphql-multipart-request-specspring-doc.cn

如果您想在应用程序中使用,您可以 通过库 multipart-spring-graphql 执行此操作。graphql-multipart-request-specspring-doc.cn

2.2. WebSocket 浏览器

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

基于 WebSocket 协议的 GraphQL

有两种这样的协议,一种在 subscriptions-transport-ws 库中,另一种在 graphql-ws 库中。前者不活跃且 由后者接替。阅读这篇博文了解历史。spring-doc.cn

有两种变体,一种用于 Spring MVC,一种用于 Spring MVC Spring WebFlux 的 Web Flux 中。两者都异步处理请求,并且具有等效的功能。 WebFlux 处理程序还使用非阻塞 I/O 和背压来流式传输消息, 这很好用,因为在 GraphQL Java 中,订阅响应是 Reactive Streams 。GraphQlWebSocketHandlerPublisherspring-doc.cn

该项目列出了许多供客户使用的配方graphql-wsspring-doc.cn

GraphQlWebSocketHandler可以通过声明 Bean 并使用它将处理程序映射到 URL 路径来公开为 WebSocket 端点。默认情况下, Boot Starter 不会通过 WebSocket 端点公开 GraphQL,但很容易 通过为 Endpoint path 添加属性来启用它。有关详细信息,请参阅 Web 端点部分,或查看 或 以获取实际的 Boot starter 配置。SimpleUrlHandlerMappingGraphQlWebMvcAutoConfigurationGraphQlWebFluxAutoConfigurationspring-doc.cn

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

2.3. RSocket

GraphQlRSocketHandler处理 GraphQL over RSocket 请求。查询和更改是 预期并作为 RSocket 交互处理,而订阅是 处理为 .request-responserequest-streamspring-doc.cn

GraphQlRSocketHandler可以使用映射到 GraphQL 请求的路由。例如:@Controllerspring-doc.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);
    }
}

2.4. 拦截

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

2.4.1.WebGraphQlInterceptor

HTTPWebSocket 传输调用一个 0 或更多 ,后跟调用 GraphQL Java 引擎。 允许应用程序拦截 incoming requests 并执行以下操作之一:WebGraphQlInterceptorExecutionGraphQlServiceWebGraphQlInterceptorspring-doc.cn

例如,拦截器可以将 HTTP 请求标头传递给 :DataFetcherspring-doc.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 Interceptor 将 HTTP 请求标头值添加到 GraphQLContext 中
2 数据控制器方法访问值

相反,拦截器可以访问控制器添加到 the 的值:GraphQLContextspring-doc.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 Controller 为GraphQLContext
2 Interceptor 使用该值添加 HTTP 响应标头

WebGraphQlHandler可以修改 ,例如,检查和修改 请求验证错误,这些错误在执行开始之前引发,并且不能 用 :ExecutionResultDataFetcherExceptionResolverspring-doc.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 如果具有非 null 值的 “data” 键,则返回相同的ExecutionResult
2 检查并转换 GraphQL 错误
3 使用修改后的错误更新ExecutionResult

用于配置链。这是支持的 通过 Boot Starter 查看 Web 端点WebGraphQlHandlerWebGraphQlInterceptorspring-doc.cn

2.4.2.RSocketQlInterceptor

WebGraphQlInterceptor 类似,它允许拦截 GraphQL over RSocket 请求在 GraphQL Java 引擎执行之前和之后。您可以使用 this 自定义 和 .RSocketQlInterceptorgraphql.ExecutionInputgraphql.ExecutionResultspring-doc.cn