使用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エラーの結果がほぼ同じかたちの応答になる。デフォルトのエラーポリシー。ignore
graphQLErrors
は無視される(graphQLErrors
がつくられない)。返されたdata
はキャッシュされて、エラーが生じなかったかのようにレンダリングされる。all
data
とerror.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链接的函数中不返回任何东西。
网络出错的情况下
在网络错误的情况下,我们建议将操作的重试添加到链接链中。您可以使用这个链接来配置指数退避和重试次数等重试逻辑。更多详细信息,请阅读「重试链接」。
错误链接选项
请参考「错误链接」中的「选项」。