You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native iOS中Apollo请求报错:Network request failed,URI浏览器可访问

React Native ApolloClient 网络请求失败调试建议

问题描述

在React Native应用中使用ApolloClient查询公开GraphQL URI时,出现TypeError: Network request failed错误,但直接在浏览器输入该URI可正常访问Schema和执行查询。已确认URI正确,替换其他公开URI可正常工作,怀疑后端问题但浏览器能正常访问,求调试方案。

客户端初始化代码

export function initServices({
  uri,
  authToken,
  mockMeanDelay = 400,
  mock = false,
  mockScenarios = [],
}: Options): Services {
  let mockRemoteController = null;
  let linkToOutsideWorld: ApolloLink;
  const messageBus = createMessageBus();

    const terminatingLink = createUploadLink({
      uri: CORRECT_URI_HERE,
    })
    const authLink = setContext(async (_, { headers }) => {
      const token = await authToken();
      return {
        headers: {
          ...headers,
          authorization: token ? `Bearer ${token}` : '',
        },
      };
    });


    linkToOutsideWorld = from([authLink, withCustomScalars(), terminatingLink]);


  const errorReportingLink = onError(({ graphQLErrors, networkError }) => {
    if (graphQLErrors)
      graphQLErrors.forEach(({ message, locations, path }) =>
        // eslint-disable-next-line no-console
        console.error(
          `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
        ),
      );
    // eslint-disable-next-line no-console
    if (networkError) console.error(`[Network error]: ${networkError}`);
  });

  const link = ApolloLink.from([errorReportingLink, linkToOutsideWorld]);

  const fragmentMatcher = new IntrospectionFragmentMatcher({
    // @ts-ignore
    introspectionQueryResultData: introspectionResult,
  });

  const apolloClient = new ApolloClient({
    link,
    defaultOptions: {
      watchQuery: {
        // We prefer using a `cache-and-network` policy so that screens
        // are always in sync with backend
        // Otherwise, the default policy would not fetch the server
        // data from server if the result of query is already in cache
        fetchPolicy: 'cache-and-network',
      },
    },
    cache: new InMemoryCache({
      cacheRedirects: {
        Query: {
          // @ts-ignore issue in typing of cacheRedirects
          userById: (_, { userId }: QueryUserByIdArgs, { getCacheKey }) =>
            getCacheKey({ __typename: 'User', id: userId }),
          // @ts-ignore  issue in typing of cacheRedirects
          gatheringSpaceById: (
            _, 
            { gatheringSpaceId }: QueryGatheringSpaceByIdArgs, 
            { getCacheKey },
          ) =>
            getCacheKey({
              __typename: 'GatheringSpace',
              id: gatheringSpaceId,
            }),
          // @ts-ignore  issue in typing of cacheRedirects
          gatheringInstanceById: (
            _, 
            { gatheringInstanceId }: QueryGatheringInstanceByIdArgs, 
            { getCacheKey },
          ) =>
            getCacheKey({
              __typename: 'GatheringInstance',
              id: gatheringInstanceId,
            }),
        },
      },
      fragmentMatcher,
    }),
  });

  return { apolloClient, messageBus, mockRemoteController };
}

调试步骤

1. 核对请求头差异

浏览器会自动添加Origin、Referer等请求头,React Native的请求头可能不同,后端可能存在头校验逻辑:

  • 在浏览器开发者工具的Network面板复制请求头
  • 在createUploadLink中手动添加这些头测试:
    const terminatingLink = createUploadLink({
      uri: CORRECT_URI_HERE,
      headers: {
        'Origin': '你的应用包名或关联域名',
        'Referer': '应用相关标识地址'
      }
    })
    

2. 验证HTTPS证书兼容性

React Native对HTTPS证书的校验比浏览器严格,尤其是自签名或过期证书:

  • 若后端用自签名证书,iOS需在Info.plist添加例外,Android需在AndroidManifest.xml配置网络安全策略
  • 调试阶段可临时关闭证书校验(生产环境禁止):
    import { Agent } from 'https';
    
    const terminatingLink = createUploadLink({
      uri: CORRECT_URI_HERE,
      fetchOptions: {
        agent: new Agent({ rejectUnauthorized: false })
      }
    })
    

3. 检查CORS配置

浏览器受CORS限制,但React Native的网络请求不受浏览器CORS规则约束,不过后端可能限制了特定Origin:

  • 确认后端CORS配置是否允许React Native请求的源(比如应用包名或通配符*)

4. 抓包对比请求细节

用Charles或Proxyman抓包,对比浏览器和React Native的请求差异:

  • 检查请求方法、请求体、头信息是否完全一致
  • 查看后端返回的状态码和原始响应内容,排查隐藏错误
  • 在代码中添加日志,确认token获取和请求头组装是否正确:
    const authLink = setContext(async (_, { headers }) => {
      const token = await authToken();
      console.log('Auth token:', token);
      const newHeaders = {
        ...headers,
        authorization: token ? `Bearer ${token}` : '',
      };
      console.log('Request headers:', newHeaders);
      return { headers: newHeaders };
    });
    

5. 测试无认证请求

暂时移除authLink,直接发送无认证请求,排查是否是认证逻辑问题:

linkToOutsideWorld = from([withCustomScalars(), terminatingLink]);

若请求成功,说明token存在过期、格式错误,或后端对Bearer token的校验逻辑与浏览器不一致

6. 确认网络环境一致性

检查React Native应用和浏览器是否处于同一网络环境(比如企业VPN、代理):

  • 在模拟器中用原生fetch测试基础连接:
    fetch(CORRECT_URI_HERE, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ query: '{ __typename }' })
    })
    .then(res => res.json())
    .then(console.log)
    .catch(console.error);
    

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 05:00:59