Vert.x Web GraphQL
Vert.x Web GraphQL 用 GraphQL-Java 库扩展了 Vert.x Web,您可以用它创建一个 GraphQL 的服务器。
提示
|
这是一个 Vert.x Web GraphQL 的参考文档。 强烈建议您先熟悉一下 GraphQL-Java API。 您可以从此处开始阅读 GraphQL-Java documentation。 |
由此开始
若要使用该模块,请在您的 Maven POM 文件的 dependencies 部分中添加以下依赖:
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web-graphql</artifactId>
<version>4.4.0</version>
</dependency>
或您使用的是Gradle:
compile 'io.vertx:vertx-web-graphql:4.4.0'
设置处理器
HTTP
为此您需要创建 Vert.x Web Route
以及 GraphQLHandler
:
GraphQL graphQL = setupGraphQLJava();
router.route("/graphql").handler(GraphQLHandler.create(graphQL));
提示
|
The
|
该处理器可处理 GET
以及 POST
请求。
当然,您也可以限制处理器仅处理其中一种 HTTP 方法:
GraphQL graphQL = setupGraphQLJava();
router.post("/graphql").handler(GraphQLHandler.create(graphQL));
重要
|
GraphQLHandler 需要 BodyHandler ,用于读取 POST 请求体内容。
|
查询批处理
查询批处理将数组(而非单个对象)通过 post 方法发送到 GraphQL 端点(endpoint)。
Vert.x Web GraphQL 可以处理此类请求,但该特性默认是被禁用的。
如要启用,需创建带参数的 GraphQLHandler
:
GraphQLHandlerOptions options = new GraphQLHandlerOptions()
.setRequestBatchingEnabled(true);
GraphQLHandler handler = GraphQLHandler.create(graphQL, options);
基于 WebSocket 的 GraphQL
Vert.x Web GraphQL 是兼容 基于 Websocket 协议的 GraphQL 的。
当您需要给您的 GraphQL schema 添加订阅时使用 websocket 进行通信非常有用,当然您也可以用其来进行查询和变更操作。
小心
|
默认情况下, 配置中并不包含一个默认的 Origin 属性。
为了防御浏览器发送的 WebSocket 跨域劫持攻击,我们建议您将 Origin 属性设置为您应用的网络源。
这会强制服务器检查 WebSocket 的 Origin 以验证连接是否来自您的应用。
这项检查非常重要,因为浏览器的同源策略并不限制 WebSocket 连接,因此一个攻击者可以轻松地从一个恶意网页连接您服务器上 GraphQL WS 处理器提供的 ws:// 或 wss:// 接口。
|
router.route("/graphql").handler(GraphQLWSHandler.create(graphQL));
重要
|
客户端会要求支持
|
提示
|
The
|
为了让 HTTP 协议和 WebSocket 协议使用同一个 URI,您必须在 GraphQLHandler
之前将 GraphQLWSHandler
注册到 Router
上:
router.route("/graphql")
.handler(GraphQLWSHandler.create(graphQL))
.handler(GraphQLHandler.create(graphQL));
重要
|
一个 DataFetcher 订阅 会返回一个 org.reactivestreams.Publisher 类型的实例。
|
GraphiQL IDE
As you are building your application, testing your GraphQL queries in GraphiQL can be handy.
To do so, create a route for GraphiQL resources and a GraphiQLHandler
for them:
GraphiQLHandlerOptions options = new GraphiQLHandlerOptions()
.setEnabled(true);
router.route("/graphiql/*").handler(GraphiQLHandler.create(options));
Then browse to http://localhost:8080/graphiql/.
注意
|
The GraphiQL user interface is disabled by default for security reasons.
This is why you must configure the GraphiQLHandlerOptions to enable it.
|
提示
|
GraphiQL is enabled automatically when Vert.x Web runs in development mode.
To switch the development mode on, use the |
If your application is protected by authentication, you can customize the headers sent by GraphiQL dynamically:
graphiQLHandler.graphiQLRequestHeaders(rc -> {
String token = rc.get("token");
return MultiMap.caseInsensitiveMultiMap().add("Authorization", "Bearer " + token);
});
router.route("/graphiql/*").handler(graphiQLHandler);
Please refer to the GraphiQLHandlerOptions
documentation for further details.
获取数据
GraphQL-Java API 非常适合异步环境:查询操作默认策略便是异步执行(对于变更,则是串行异步)。
为 避免阻塞 eventloop ,您需要做的是实现 data fetchers 并返回 CompletionStage
,而不是直接返回结果。(译者注: Data Fetcher 下文统一保留原文、不翻译)
DataFetcher<CompletionStage<List<Link>>> dataFetcher = environment -> {
Future<List<Link>> future = retrieveLinksFromBackend(environment);
return future.toCompletionStage();
};
RuntimeWiring runtimeWiring = RuntimeWiring.newRuntimeWiring()
.type("Query", builder -> builder.dataFetcher("allLinks", dataFetcher))
.build();
提示
|
Instead of converting Vert.x First, declare the instrumentation while configuring GraphQL-Java.
Then you can return Vert.x futures directly.
|
为数据获取器提供上下文
GraphQLHandler
通常是最后一个声明的处理器。
例如,您可以通过身份验证保护您的应用程序。
在这种情况下,您的 DataFetcher 可能需要知道哪个用户已登录,才能缩小结果范围。
假设您的身份验证层在 RoutingContext
中存储了一个 User
对象。
您可以通过 DataFetchingEnvironment
来获取这个 User
对象:
DataFetcher<CompletionStage<List<Link>>> dataFetcher = environment -> {
RoutingContext routingContext = GraphQLHandler.getRoutingContext(environment.getGraphQlContext());
User user = routingContext.get("user");
Future<List<Link>> future = retrieveLinksPostedBy(user);
return future.toCompletionStage();
};
JSON 类型数据结果
The default GraphQL data fetcher is the PropertyDataFetcher
.
It is able to read the fields of your domain objects without further configuration.
Nevertheless, in Vert.x applications it is common to work with JsonArray
and JsonObject
.
The PropertyDataFetcher
can read the items in a JsonArray
out of the box, but not the fields of a JsonObject
.
The solution to this problem depends on your GraphQL-Java version.
注意
|
Both solutions let you mix JsonObject , JsonArray and domain objects results.
|
GraphQL-Java 20 and later
Configure GraphQL-Java with the JsonObjectAdapter
instrumentation.
graphQLBuilder.instrumentation(new JsonObjectAdapter());
GraphQL-Java 19 and before
Configure GraphQL-Java to use VertxPropertyDataFetcher
instead:
RuntimeWiring.Builder builder = RuntimeWiring.newRuntimeWiring();
builder.wiringFactory(new WiringFactory() {
@Override
public DataFetcher<Object> getDefaultDataFetcher(FieldWiringEnvironment environment) {
return VertxPropertyDataFetcher.create(environment.getFieldDefinition().getName());
}
});
批量加载
DataLoader 可以批量化请求并缓存结果,助您高效地加载数据。(译者注:与 DataFetcher 对应,DataLoader保留原文不翻译)
首先,创建一个批量加载器:
BatchLoaderWithContext<String, Link> linksBatchLoader = (ids, env) -> {
// retrieveLinksFromBackend takes a list of ids and returns a CompletionStage for a list of links
return retrieveLinksFromBackend(ids, env);
};
然后,配置 GraphQLHandler
,为每个请求创建一个 DataLoaderRegistry
:
GraphQLHandler handler = GraphQLHandler.create(graphQL).beforeExecute(builderWithContext -> {
DataLoader<String, Link> linkDataLoader = DataLoaderFactory.newDataLoader(linksBatchLoader);
DataLoaderRegistry dataLoaderRegistry = new DataLoaderRegistry().register("link", linkDataLoader);
builderWithContext.builder().dataLoaderRegistry(dataLoaderRegistry);
});
文件上传
GraphQL multipart 请求 是用于 GraphQL
请求的可互操作表单结构。
通过启用此功能,GraphQL 客户端将能够使用单个变更(Mutation)调用来上传文件。
所有服务器端文件处理逻辑都抽象到了 GraphQLHandler
。
启用该功能需要创建一个 GraphQLHandler
,并将 requestMultipartEnabled 配置设置为true,然后添加 BodyHandler
到路由。
GraphQLHandler graphQLHandler = GraphQLHandler.create(
setupGraphQLJava(),
new GraphQLHandlerOptions().setRequestMultipartEnabled(true)
);
Router router = Router.router(vertx);
router.route().handler(BodyHandler.create());
router.route("/graphql").handler(graphQLHandler);
重要
|
如果路由没有 BodyHandler ,则 multipart 请求解析器无法处理 GraphQL 的变更请求。
|
最后,创建 Upload
scalar 并设置到 RuntimeWiring
中:
RuntimeWiring runtimeWiring = RuntimeWiring.newRuntimeWiring().scalar(UploadScalar.build()).build();
FileUpload
实例可以通过 DataFetchingEnvironment::getArgument
方法获取。
FileUpload file = environment.getArgument("myFile");
RxJava 3 API
配置 Rxified 路由
若需要在 Rxified 路由
中处理 GraphQL 请求,则请确保已导入了 GraphQLHandler
类。
使用 Vert.x 的 Rxified API
GraphQL-Java 期望 DataFetcher 和批量加载器使用 CompletionStage
提供异步结果。
但是若您使用 Vert.x Rxified API (如 Web 客户端),则需要适配 Single
和 Maybe
对象。
DataFetcher<CompletionStage<String>> fetcher = environment -> {
Single<String> data = loadDataFromBackend();
return data.toCompletionStage();
};
提示
|
Instead of converting First, declare the instrumentations while configuring GraphQL-Java.
Then you can return
|