Apollo Client中RetryLink无法读取响应headers的问题咨询
问题原因
- Apollo Link 执行顺序是请求从数组第一个链路向后传递到最末端的HTTP链路,响应/错误从末端向前冒泡返回。你当前链路里
RetryLink放在最前面,httpLink在最后,429这类错误响应是从httpLink直接沿错误通道向上抛出的。 - 普通200响应会被
httpLink自动挂载到operation的context上,所以你写的afterware在map回调里能读到headers;但错误响应不会自动写入context,等错误传到RetryLink触发delay/attempts回调时,context里自然没有带headers的response对象。 - 官方
RetryLink本身不会主动从抛出的错误对象里提取response信息写入context,所以直接读context拿不到值。 - 另外你当前响应是cors类型,如果服务端没把需要读取的头加到
Access-Control-Expose-Headers里,哪怕拿到response对象,浏览器也会拦截头读取,表现为Headers对象为空。
解决步骤
- 新增一个中间链路,放在
RetryLink和authLink/httpLink之间,不管是正常响应还是错误响应,都提前把response对象写入operation的context,再把结果/错误继续向上传递:
const injectResponseContextLink = new ApolloLink((operation, forward) => { return forward(operation) .map(response => { // 正常响应写入context operation.setContext(prev => ({ ...prev, response: response.response })); return response; }) .catch(err => { // 错误响应也写入context if (err.response) { operation.setContext(prev => ({ ...prev, response: err.response })); } throw err; }); });
- 调整Apollo Client的链路顺序,确保响应/错误先经过上面的注入链路,再到达
RetryLink:
const createApolloClient = () => { return new ApolloClient({ ssrMode: typeof window === 'undefined', // 链路顺序从前往后:重试逻辑 -> 响应注入 -> 鉴权 -> HTTP请求 link: from([retryLink, injectResponseContextLink, authLink.concat(httpLink)]), cache: new InMemoryCache() }); };
- 调整
RetryLink的逻辑,优先读取服务端返回的Retry-After头,兜底走原有随机退避逻辑即可:
const retryLink = new RetryLink({ attempts: (count, _, error) => { // 建议只对429限流错误做重试,其他错误直接抛出 return count <= 5 && !!error && error.statusCode === 429; }, delay: (count, operation) => { const { response } = operation.getContext(); if (response?.headers) { const retryAfter = response.headers.get('Retry-After'); // Retry-After如果返回的是秒数直接转毫秒,是日期格式的话可以自己加转换逻辑 if (retryAfter && !isNaN(Number(retryAfter))) { return Number(retryAfter) * 1000; } } return count * 1000 * Math.random(); } });
额外校验项
跨域场景下让后端把Retry-After以及你需要读取的其他自定义响应头,加入CORS配置的Access-Control-Expose-Headers列表,否则浏览器会出于安全限制禁止JS读取这些头,哪怕请求实际返回了头你也拿不到。
内容的提问来源于stack exchange,提问作者pgr3931
相关产品推荐
相关产品推荐

