Apollo错误处理:如何区分503与其他5xx错误
问题
我有一个使用Apollo从GraphQL端点获取数据的React应用。我已设置error link方法,当GQL返回HTTP错误时展示不同消息,这在处理4xx错误时正常工作。但当服务器返回5xx错误时,我在方法中仅得到通用的“failed to fetch”错误消息,无法获取错误码。
503错误通常表示系统处于维护模式,因此我希望能区分503和其他5xx错误以展示不同消息。是否可以调整错误处理逻辑来获取5xx错误的状态码?我在传入方法的errorResponse中未发现明显相关内容。
以下是我的onError link简化版本:
const errorLink = onError((errorResponse) => { const { graphQLErrors, networkError, operation } = errorResponse; // Doesn't work - networkError is null. if (( networkError?.statusCode === 503 ) && !window.location.pathname.includes('maintenance-mode') ) { // [show maintenance mode page] } // This works. if (networkError?.statusCode === 401 && operation?.operationName !== 'auth') { // [redirect to login page] } // [...etc] });
解决方案
问题出在Apollo Client默认的fetch实现对5xx错误的处理上——当服务器返回5xx状态码时,它会抛出通用的TypeError: failed to fetch,不会将错误包装成带状态码的networkError对象。你可以通过以下两种方式解决:
1. 自定义fetch策略(推荐)
替换Apollo Client使用的fetch方法,手动处理HTTP错误,将状态码附加到错误对象中:
import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client'; import { onError } from '@apollo/client/link/error'; // 自定义fetch函数 const customFetch = async (uri, options) => { const response = await fetch(uri, options); // 非2xx响应时手动抛出带状态码的错误 if (!response.ok) { const error = new Error(response.statusText); error.statusCode = response.status; throw error; } return response; }; // 创建HTTP link时使用自定义fetch const httpLink = createHttpLink({ uri: '你的GraphQL端点地址', fetch: customFetch, }); // 错误处理link保持原有逻辑,此时networkError会包含statusCode const errorLink = onError((errorResponse) => { const { networkError, operation } = errorResponse; if (networkError?.statusCode === 503 && !window.location.pathname.includes('maintenance-mode')) { // 展示维护页面的逻辑 } if (networkError?.statusCode === 401 && operation?.operationName !== 'auth') { // 跳转到登录页的逻辑 } }); // 组合links并创建Client实例 const client = new ApolloClient({ link: errorLink.concat(httpLink), cache: new InMemoryCache(), });
2. 解析networkError的底层错误对象
如果不想替换整个fetch,可尝试从networkError的cause属性中获取原始响应(兼容性取决于运行环境):
const errorLink = onError((errorResponse) => { const { networkError, operation } = errorResponse; let statusCode; // 尝试从错误的cause属性中解析状态码 if (networkError?.cause instanceof Response) { statusCode = networkError.cause.status; } else if (networkError?.statusCode) { statusCode = networkError.statusCode; } if (statusCode === 503 && !window.location.pathname.includes('maintenance-mode')) { // 展示维护页面 } if (statusCode === 401 && operation?.operationName !== 'auth') { // 跳转登录页 } });
注意:第二种方法的兼容性有限,部分旧浏览器可能不支持cause属性,因此第一种自定义fetch的方式更可靠。
内容的提问来源于stack exchange,提问作者Heather Gaye
相关产品推荐
相关产品推荐

