高级 HTTP 网络技术
使用 Apollo Link 完全控制网络
该 Apollo Link 库让您可以精确控制由 Apollo 客户端 发送的 HTTP 请求。您还可以使用它来用完全定制的网络层(如 WebSocket 转发或模拟服务器数据)替换 Apollo 客户端的网络层。
当使用 Apollo Link 时,您将定义网络行为为一系列 链 对象的集合,这些对象按顺序执行以控制数据流。默认情况下, Apollo 客户端 使用 Apollo Link 的 HttpLink
以发送 HTTP 上的 GraphQL 查询。
Apollo Link 包含各种支持使用情况的可安装链接,您也可以创建自己的自定义链接。
自定义请求逻辑
以下示例演示了向 Apollo 客户端 添加自定义链接。此链接在 HttpLink
发送之前向每个 HTTP 请求添加一个 Authorization
标头:
import { ApolloClient, HttpLink, ApolloLink, InMemoryCache, concat } from '@apollo/client';const httpLink = new HttpLink({ uri: '/graphql' });const authMiddleware = new ApolloLink((operation, forward) => {// add the authorization to the headersoperation.setContext(({ headers = {} }) => ({headers: {...headers,authorization: localStorage.getItem('token') || null,}}));return forward(operation);})const client = new ApolloClient({cache: new InMemoryCache(),link: concat(authMiddleware, httpLink),});
以下示例展示了在数组中提供多个自定义链接
import { ApolloClient, HttpLink, ApolloLink, InMemoryCache, from } from '@apollo/client';const httpLink = new HttpLink({ uri: '/graphql' });const authMiddleware = new ApolloLink((operation, forward) => {// add the authorization to the headersoperation.setContext(({ headers = {} }) => ({headers: {...headers,authorization: localStorage.getItem('token') || null,}}));return forward(operation);})const activityMiddleware = new ApolloLink((operation, forward) => {// add the recent-activity custom header to the headersoperation.setContext(({ headers = {} }) => ({headers: {...headers,'recent-activity': localStorage.getItem('lastOnlineTime') || null,}}));return forward(operation);})const client = new ApolloClient({cache: new InMemoryCache(),link: from([authMiddleware,activityMiddleware,httpLink]),});
在上面的示例中,authMiddleware
链接设置了每个请求的 Authorization
头,然后 activityMiddleware
会设置每个请求的 Recent-Activity
头。最后,HttpLink
会发送修改后的请求。
自定义响应逻辑
您可以使用 Apollo Link 在 Apollo Client 收到请求的响应时自定义其行为。
以下示例展示了使用 @apollo/client/link/error
处理包含在响应中的网络错误:
import { ApolloClient, InMemoryCache, HttpLink } from '@apollo/client';import { onError } from '@apollo/client/link/error';import { logout } from './logout';const httpLink = new HttpLink({ uri: '/graphql' });const logoutLink = onError(({ networkError }) => {if (networkError.statusCode === 401) logout();})const client = new ApolloClient({cache: new InMemoryCache(),link: logoutLink.concat(httpLink),});
在这个示例中,如果服务器返回 401
代码(未经授权),用户会从应用程序中登出。
修改响应数据
您可以创建一个自定义链接来编辑或删除 fields 从 response.data
中。为此,您需要调用链接的 forward(operation)
调用的结果上的 map
。在 map
函数中,对 response.data
进行所需的更改,然后返回它:
import { ApolloClient, InMemoryCache, HttpLink, ApolloLink } from '@apollo/client';const httpLink = new HttpLink({ uri: '/graphql' });const formatDateLink = new ApolloLink((operation, forward) => {return forward(operation).map(response => {if (response.data.date) {response.data.date = new Date(response.data.date);}return response;});});const client = new ApolloClient({cache: new InMemoryCache(),link: formatDateLink.concat(httpLink),});
在上面的示例中,formatDateLink
将每个响应的最高层中的一个 date
field 字段更改为 JavaScript 日期对象。
请注意,forward(operation).map(func)
不支持异步执行 func
映射函数。如果需要执行异步修改,使用 @apollo/client/utilities
中的 asyncMap
函数,如下所示:
import {ApolloClient,InMemoryCache,HttpLink,ApolloLink} from "@apollo/client";import { asyncMap } from "@apollo/client/utilities";import { usdToEur } from './currency';const httpLink = new HttpLink({ uri: '/graphql' });const usdToEurLink = new ApolloLink((operation, forward) => {return asyncMap(forward(operation), async (response) => {let data = response.data;if (data.price && data.currency === "USD") {data.price = await usdToEur(data.price);data.currency = "EUR";}return response;});});const client = new ApolloClient({cache: new InMemoryCache(),link: usdToEurLink.concat(httpLink)});
在上面的示例中,usdToEurLink
使用 asyncMap
将响应对象的 price
字段从美元转换为欧元,使用外部 API。
虽然此技术可用来修改 现有 字段(或在 data
列表中添加额外的对象)是有用的,但在大多数情况下,将新字段添加到 data
中将不起作用,因为在 ApolloLink
请求流水线内无法安全地修改操作文档。
HttpLink
对象
Apollo客户端使用HttpLink来通过HTTP向服务器发送GraphQL操作。该链路支持POST和GET请求,并且可以根据单个查询修改HTTP选项。这在实现身份验证、持久化查询、动态URI和其他细粒度更新时非常方便。
用法
import { HttpLink } from "@apollo/client";const link = new HttpLink({ uri: "/graphql" });
构造函数选项
HttpLink构造函数接受以下选项:
选项 | 描述 |
---|---|
uri | 一个字符串终点或函数,该函数解析您想要执行操作的GraphQL服务器。(默认:/graphql ) |
includeExtensions | 如果为true ,则可以传递一个extensions 字段到您的GraphQL服务器。(默认:false ) |
fetch | 一个用于发起请求的兼容fetch 的API。请参阅为特定环境提供fetch 替换。 |
headers | 一个包含要包含在每次请求中的头部名称和值的对象。 |
preserveHeaderCase | 如果为true ,则头部值将保留其大写字母形式用于非HTTP规范的服务器。(默认:false ) |
credentials | 一个表示用于fetch 调用的凭证策略的字符串。(有效值:omit 、include 、same-origin ) |
fetchOptions | 包含此选项以覆盖传递给fetch 调用的某些选项的值。 |
useGETForQueries | 如果为true ,则HttpLink 使用GET 请求而不是使用POST 请求来执行查询操作(但不执行突变操作)。(默认:false ) |
print | 一个函数,用于自定义请求中的AST格式化。请参阅重写默认的print函数。 |
为特定环境提供fetch
替换
HttpLink
需要您的运行时环境中存在fetch
。这对于React Native和大多数现代浏览器是正确的。如果您针对的环境不包含不包含fetch
(例如较古老的浏览器或服务器),您需要通过构造函数选项将其自己的fetch
传递给HttpLink
。我们推荐使用cross-fetch
用于较旧的浏览器和Node。
覆盖默认的 print
函数
print
选项用于自定义在对象发送到网络之前如何将 DocumentNode
对象转换回字符串。如果没有提供自定义 print
函数,则会使用 GraphQL print
函数。自定义 print
函数应接受一个 ASTNode
(通常是一个 DocumentNode
),以及原始 print
函数作为参数,并返回一个字符串。此选项可以与 stripIgnoredCharacters
一起使用,以从查询中删除空格:
import { ASTNode, stripIgnoredCharacters } from 'graphql';const httpLink = new HttpLink({uri: '/graphql',print(ast: ASTNode,originalPrint: (ast: ASTNode) => string,) {return stripIgnoredCharacters(originalPrint(ast));},});
覆盖选项
您可以通过修改操作 context
对象以基于操作覆盖大多数 HttpLink
构造函数选项。例如,您可以将 headers
字段设置为操作以传递特定操作的自定义标题。 context
还支持 credentials
字段,用于定义凭证策略,使用 uri
动态更改端点,以及 fetchOptions
以允许通用的 fetch 覆盖(例如, method: "GET"
)。
请注意,如果您将 fetchOptions.method
设置为 GET
, HttpLink
将遵循 标准 GraphQL HTTP GET 编码。查询、('变量)、操作名和扩展都将作为查询参数传递,而不是放在 HTTP 请求体中(因为没有请求体)。如果您想继续以非幂等的 mutate 作为 POST
请求发送,请将顶层 useGETForQueries
选项设置为 true
,而不是将 fetchOptions.method
设置为 GET
。
HttpLink
也会将 fetch
操作的响应附加到上下文中作为 response
,这样您就可以在其他链接中访问它。
上下文选项
选项 | 描述 |
---|---|
headers | 一个包含要包含在每次请求中的头部名称和值的对象。 |
credentials | 一个表示用于fetch 调用的凭证策略的字符串。(有效值:omit 、include 、same-origin ) |
uri | 一个字符串端点或函数,该端点或函数解析为您想要执行操作的服务器。 |
fetchOptions | 传递到 fetch 调用的-fetch 选项的任何覆盖。 |
response | 在执行之后从 fetch 请求的原始响应。 |
http | 一个对象,让您可以控制 HttpLink 本身的细微方面,如持久查询(见下文)。 |
以下示例显示了如何使用 context
将特殊头传递给单个 查询:
import { ApolloClient, InMemoryCache } from "@apollo/client";const client = new ApolloClient({cache: new InMemoryCache(),uri: "/graphql"});client.query({query: MY_QUERY,context: {// example of setting the headers with context per operationheaders: {special: "Special header value"}}});
自定义获取
HttpLink
's fetch
选项可以用来联接自定义网络。如果您想根据计算出的头修改请求,或根据操作来计算 URI,这非常有用。例如:
自定义认证
const customFetch = (uri, options) => {const { header } = Hawk.client.header("http://example.com:8000/resource/1?b=1&a=2","POST",{ credentials: credentials, ext: "some-app-data" });options.headers.Authorization = header;return fetch(uri, options);};const link = new HttpLink({ fetch: customFetch });
动态 URI
const customFetch = (uri, options) => {const { operationName } = JSON.parse(options.body);return fetch(`${uri}/graph/graphql?opname=${operationName}`, options);};const link = new HttpLink({ fetch: customFetch });
使用其他链接
Apollo Link 包含许多针对特定使用情况的链接,例如用于通过 WebSocket 通信的 WebSocketLink
和用于将多个 GraphQL 操作组合到单个 HTTP 请求中的 BatchHttpLink
。
要了解更多有关它所提供的功能,请参阅 Apollo Link API 文档。