You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何全局处理Apollo查询与变更错误?400状态码问题排查

我太懂你这种头疼的情况了——格式不对的GraphQL查询/变更在React Native里直接抛出未处理的400网络错误,明明GraphiQL能正常返回错误详情,但本地就是抓不到,完全没法优雅处理对吧?别着急,下面给你几个亲测有效的全局处理方案,一步步解决这个问题:

全局处理Apollo查询/变更错误的实用方案

1. 用Apollo的onError链接做全局拦截(最推荐)

这是Apollo官方推荐的全局错误处理方式,通过apollo-link-error可以拦截所有GraphQL请求的错误,包括网络错误(比如400状态码)和GraphQL自身的字段错误。

实现步骤:

首先确保你已经安装了相关依赖(如果没装的话):

npm install @apollo/client apollo-link-error
# 或者用yarn
yarn add @apollo/client apollo-link-error

然后在创建Apollo Client实例时配置错误链接:

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

// 定义全局错误处理逻辑
const errorLink = onError(({ graphQLErrors, networkError }) => {
  // 处理GraphQL自身的错误(比如字段不存在、权限问题)
  if (graphQLErrors) {
    graphQLErrors.forEach(({ message, path }) => {
      console.log(`GraphQL字段错误 [路径: ${path}]: ${message}`);
      Alert.alert('查询错误', message);
    });
  }

  // 处理网络错误(比如400、500状态码)
  if (networkError) {
    console.log('全局捕获网络错误:', networkError);
    // 针对400状态码做专门处理
    if (networkError.statusCode === 400) {
      Alert.alert('请求错误', '查询/变更格式不正确,请检查语法');
      // 如果你想阻止错误继续向上抛出(避免触发React的未处理错误),可以在这里做处理
    }
  }
});

// 组合所有链接并创建Client
const httpLink = new HttpLink({ uri: '你的GraphQL端点地址' });
const client = new ApolloClient({
  cache: new InMemoryCache(),
  link: from([errorLink, httpLink]) // 错误链接要放在http链接前面,才能拦截错误
});

这个方案的好处是全局生效,不管你用useQuery还是useMutation,所有请求的错误都会经过这里处理,而且能精准区分网络错误和GraphQL业务错误。

2. 用React错误边界做兜底捕获

如果Apollo的错误链接没拦住(比如某些特殊场景),可以用React的错误边界组件来兜底,避免应用直接崩溃,同时捕获未处理的错误。

实现错误边界组件:

import React from 'react';
import { View, Text, Alert } from 'react-native';

class ApolloErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError() {
    // 更新状态,显示错误UI
    return { hasError: true };
  }

  componentDidCatch(error) {
    // 捕获到错误后做处理
    console.log('错误边界捕获到未处理错误:', error);
    // 判断是否是Apollo的400网络错误
    if (error.message.includes('Network error: Response not successful: Received status code 400')) {
      Alert.alert('请求失败', '查询格式有误,请检查后重试');
    }
  }

  render() {
    if (this.state.hasError) {
      // 自定义错误提示UI
      return (
        <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
          <Text>抱歉,出现了一些问题,请稍后重试</Text>
        </View>
      );
    }
    // 正常渲染子组件
    return this.props.children;
  }
}

export default ApolloErrorBoundary;

然后在App的根组件里用它包裹ApolloProvider

import { ApolloProvider } from '@apollo/client';
import ApolloErrorBoundary from './ApolloErrorBoundary';
import client from './apolloClient';

export default function App() {
  return (
    <ApolloErrorBoundary>
      <ApolloProvider client={client}>
        {/* 你的应用页面组件 */}
      </ApolloProvider>
    </ApolloErrorBoundary>
  );
}

3. 封装自定义Hook统一处理错误(局部+全局结合)

如果你想更灵活地控制每个查询/变更的错误处理,同时保持统一逻辑,可以封装自定义的useQueryuseMutation Hook:

import { useQuery as useApolloQuery, useMutation as useApolloMutation } from '@apollo/client';
import { Alert } from 'react-native';

// 封装安全查询Hook
export const useSafeQuery = (query, options = {}) => {
  const { data, loading, error, ...rest } = useApolloQuery(query, options);

  if (error) {
    handleApolloError(error);
  }

  return { data, loading, error, ...rest };
};

// 封装安全变更Hook
export const useSafeMutation = (mutation, options = {}) => {
  const [mutate, result] = useApolloMutation(mutation, options);

  const safeMutate = async (...args) => {
    try {
      const response = await mutate(...args);
      return response;
    } catch (error) {
      handleApolloError(error);
      throw error; // 如果需要上层组件继续处理,可以抛出错误
    }
  };

  return [safeMutate, result];
};

// 统一的错误处理函数
const handleApolloError = (error) => {
  console.log('Apollo请求错误:', error);
  if (error.networkError?.statusCode === 400) {
    Alert.alert('请求错误', '查询/变更格式不正确,请检查语法');
  } else if (error.graphQLErrors) {
    const errorMsg = error.graphQLErrors[0]?.message || '请求失败';
    Alert.alert('查询错误', errorMsg);
  }
};

之后在组件里直接用这些自定义Hook代替原生的:

import { useSafeQuery } from './hooks';
import { GET_USER_QUERY } from './queries';

function UserScreen() {
  const { data, loading } = useSafeQuery(GET_USER_QUERY);
  
  if (loading) return <Text>加载中...</Text>;
  
  return <Text>用户名: {data?.user?.name}</Text>;
}
总结

优先推荐用**方案1(onError链接)做全局拦截,配合方案2(错误边界)**做兜底,这样绝大多数错误都能被优雅处理。如果需要更灵活的局部控制,再结合方案3的自定义Hook。

内容的提问来源于stack exchange,提问作者Simon

火山引擎 最新活动