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
相关产品推荐
相关产品推荐

