使用React和TypeScript:在Apollo Client中处理操作错误

Apollo Client是一种在React中使用的状态管理库。可以使用GraphQL处理本地和远程数据。本文根据官方网站上的“处理操作错误”介绍了如何处理操作错误。假设您已经学习了在Apollo Client中使用查询的基础知识(如果还没有,请先阅读“React + TypeScript:使用Apollo Client的GraphQL查询”)。这不是文档的翻译版本,而是通过重新用日语解释的方式来说明。省略了原文中的部分内容,并对不容易理解的部分进行了补充。

当Apollo Client在GraphQL服务器上执行操作时,可能会遇到各种错误。为了能正确处理不同类型的错误,当发生错误时,Apollo Client会提供适当的信息。

错误类型

当在远程服务器上操作GraphQL时,可能会出现以下两种错误。

    • GraphQLエラー

 

    ネットワークエラー

GraphQL错误

这与GraphQL操作在服务器端的执行有关。出现以下错误。

構文エラー: クエリの形式が正しくない場合など。

検証エラー: クエリに存在しないスキーマフィールドが含まれていた場合など。

リゾルバエラー: クエリフィールドをつくろうとしてエラーが発生した場合など。

如果出现语法错误或验证错误,服务器将不会执行任何操作。这是因为操作是无效的。当出现解析器错误时,服务器可能会返回部分数据。有关GraphQL错误的更多信息,请参阅“错误处理”部分。

若发生GraphQL错误,服务器会将该错误包含在响应的Apollo Client的errors数组中。以下代码是错误响应的示例。

{
	"errors": [
		{
			"message": "Cannot query field \"nonexistentField\" on type \"Query\".",
			"locations": [
				{
					"line": 2,
					"column": 3
				}
			],
			"extensions": {
				"code": "GRAPHQL_VALIDATION_FAILED",
				"exception": {
					"stacktrace": [
						"GraphQLError: Cannot query field \"nonexistentField\" on type \"Query\".",
						"...additional lines..."
					]
				}
			}
		}
	],
	"data": null
}

Apollo Client将这些错误添加到返回的useQuery调用(或使用操作钩子)返回的error.graphQLErrors数组中。

如果Apollo服务器由于GraphQL错误无法执行操作,则会以4xx状态码响应。当发生解析器错误时,如果响应中包含部分数据,响应的状态码将为200。

出现错误的部分数据。

即使在引发解析错误的操作中,也可能返回部分数据。这意味着只有部分请求的数据包含在服务器的响应中。Apollo Client默认情况下会忽略部分数据。通过设置下面提到的”GraphQL错误策略”,可以覆盖这种行为。

网络错误

当尝试连接GraphQL服务器时,会出现网络错误。通常,响应状态码为4xx或5xx(无数据)。

当发生网络错误时,Apollo Client会将其添加到useQuery调用(或使用的操作钩子)返回的error.networkError字段中。

请在应用程序中使用下文提到的Apollo Link,以便进行再试行逻辑和其他高级网络错误处理。

GraphQL错误策略

如果在GraphQL操作中出现一个或多个解析器错误,服务器的响应可能包含部分数据在data字段中。

{
	"data": {
		"getInt": 12,
		"getString": null
	},
	"errors": [
		{
			"message": "Failed to get string!"
			// ...その他のフィールド...
		}
	]
}

默认情况下,Apollo Client会丢弃部分数据,并在useQuery调用(或使用的Hook)的结果中创建error.graphQLErrors数组。操作错误策略是为了使用部分结果。

Apollo Client提供以下操作的错误策略支持。

ポリシー説明noneレスポンスにGraphQLエラーが含まれていると、error.graphQLErrorsとして返される。レスポンスのdataは、サーバーがdataを返したとしても、undefinedに定められる。結果として、ネットワークエラーとGraphQLエラーの結果がほぼ同じかたちの応答になる。デフォルトのエラーポリシー。ignoregraphQLErrorsは無視される(graphQLErrorsがつくられない)。返されたdataはキャッシュされて、エラーが生じなかったかのようにレンダリングされる。alldataerror.graphQLErrorsがともにつくられる。部分データとエラー情報のどちらもレンダリングできる。

设定错误策略

错误策略需要通过操作钩子(例如useQuery等)的选项对象来设定,具体如下。

const MY_QUERY = gql`
	query WillFail {
		badField # このフィールドのリゾルバはエラーを起こした
		goodField # このフィールドは正しくつくられた
	}
`;
function ShowingSomeErrors() {
	const { loading, error, data } = useQuery(MY_QUERY, { errorPolicy: 'all' });
	if (loading) return <span>loading...</span>;
	return (
		<div>
			<h2>Good: {data.goodField}</h2>
			<pre>
				Bad:{' '}
				{error.graphQLErrors.map(({ message }, i) => (
					<span key={i}>{message}</span>
				))}
			</pre>
		</div>
	);
}

在这个代码示例中,使用了”all”错误策略。如果可能的话,将渲染部分数据和错误信息。

使用Apollo Link进行高级错误处理。

在Apollo Link库中,可以配置对操作执行过程中发生的错误进行高级处理。

作为推荐的第一步,您可以将onError链接添加到链接链中,这样可以接收错误的详细信息,并根据其进行操作。

以下的代码示例将两个链接包含在ApolloClient构造函数中的链接链中传递。

onError: graphQLErrorsまたはnetworkErrorがサーバーのレスポンスにあるかどうか確かめる。エラーがあれば、その詳細はログに記録される。

HttpLink: 各GraphQL操作をサーバーに送信する。

チェーンの終端リンク。

import { ApolloClient, HttpLink, InMemoryCache, from } from '@apollo/client';
import { onError } from '@apollo/client/link/error';

const errorLink = onError(({ graphQLErrors, networkError }) => {
	if (graphQLErrors)
		graphQLErrors.forEach(({ message, locations, path }) =>
			console.log(
				`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
			)
		);
	if (networkError) console.log(`[Network error]: ${networkError}`);
});
const httpLink = new HttpLink({ uri: 'http://localhost:4000/graphql' })
const client = new ApolloClient({
	cache: new InMemoryCache(),
	link: from([errorLink, httpLink]),
});

重新尝试操作

Apollo Link可以在操作失败时进行重试,以解决可能存在的问题。根据发生的错误类型,您可以使用不同的链接进行处理。

onErrorはGraphQLエラー。

RetryLinkはネットワークエラー。

如果发生GraphQL错误。

当出现错误时,您可以根据返回的GraphQL错误类型重新尝试失败的操作。例如,如果使用令牌进行身份验证,可以在令牌过期时自动处理重新认证。

请在onError函数内返回forward(operation)以重新尝试操作。以下是代码示例:

onError(({ graphQLErrors, networkError, operation, forward }) => {
	if (graphQLErrors) {
		for (let err of graphQLErrors) {
			switch (err.extensions.code) {
				// AuthenticationErrorリゾルバにスローされると
				// Apollo ServerはcodeにUNAUTHENTICATEDを設定
				case 'UNAUTHENTICATED':
					// Modify the operation context with a new token
					const oldHeaders = operation.getContext().headers;
					operation.setContext({
						headers: {
							...oldHeaders,
							authorization: getNewToken(),
						},
					});
					// リクエストを再試行してオブザーバブルが新たに返される
					return forward(operation);
			}
		}
	}
	// ネットワークエラーの再試行にはonErrorリンクでなくRetryLinkが推奨
	// ここではエラーのログのみ行う
	if (networkError) {
		console.log(`[Network error]: ${networkError}`);
	}
});

[备注] 如果再次尝试的操作出现更多错误,将不会传递到onError链接,并且不会发生操作的无限循环。换句话说,onError链接只能对特定的操作进行一次重新尝试。

如果你不想重新尝试操作,请在onError链接的函数中不返回任何东西。

网络出错的情况下

在网络错误的情况下,我们建议将操作的重试添加到链接链中。您可以使用这个链接来配置指数退避和重试次数等重试逻辑。更多详细信息,请阅读「重试链接」。

错误链接选项

请参考「错误链接」中的「选项」。

广告
将在 10 秒后关闭
bannerAds