Apollo Client上下文无请求头,Cookie认证失效问题排查
问题分析与修复方案
核心问题拆解
- authLink逻辑依赖无效的初始headers:你的
authLink优先检查传入的headers?.authorization,但这是链路的第一个环节,初始请求不存在预设headers,导致该判断永远不生效,必然进入NO TOKEN分支。 getCookie未适配SSR/客户端环境:Next.js中服务端无法访问document.cookie,必须从请求头的Cookie字段解析;若你的getCookie未区分环境,或用了不必要的异步实现,会导致token读取失败。- SSR场景未传递请求上下文:使用
NextSSRApolloClient时,SSR阶段需将包含Cookie的请求对象传递给链路,否则服务端无法获取Cookie中的token。
修复步骤
1. 修正authLink逻辑,移除无效判断
直接从Cookie获取token,无需依赖初始headers:
const authLink = setContext(async (_, { headers }) => { // 直接从Cookie读取token,跳过无效的existingToken检查 const token = await getCookie(ACCESS_TOKEN_COOKIE_NAME); const authorization = token ? `Bearer ${token.value}` : undefined; return { headers: { ...headers, // token不存在时不传空字符串,避免覆盖默认headers ...(authorization && { authorization }), }, }; });
2. 完善getCookie函数,适配双环境
修改getCookie,支持服务端传入request对象:
// cookies.js export const getCookie = (name, req) => { // 服务端场景:从request的Cookie头解析 if (req) { const cookieHeader = req.headers.cookie; if (!cookieHeader) return null; const cookies = cookieHeader.split(';').reduce((acc, cookie) => { const [key, value] = cookie.trim().split('='); acc[key] = decodeURIComponent(value); return acc; }, {}); return cookies[name] ? { value: cookies[name] } : null; } // 客户端场景:从document.cookie解析 const value = `; ${document.cookie}`; const parts = value.split(`; ${name}=`); if (parts.length === 2) return { value: decodeURIComponent(parts.pop().split(';').shift()) }; return null; };
3. 链路函数接收SSR请求上下文
让getApolloClientLink接收request参数,在SSR时传递给getCookie:
export const getApolloClientLink = (req) => { // 新增req参数 const httpLink = new HttpLink({ uri: `${apiBaseUrl}/graphql`, credentials: "same-origin", }); const authLink = setContext(async (_, { headers }) => { // 传入req到getCookie,适配SSR场景 const token = await getCookie(ACCESS_TOKEN_COOKIE_NAME, req); const authorization = token ? `Bearer ${token.value}` : undefined; return { headers: { ...headers, ...(authorization && { authorization }), }, }; }); return authLink.concat(httpLink); };
4. 注册Client时传递SSR上下文
修改registerApolloClient逻辑,将SSR的request传入链路:
export const { getClient } = registerApolloClient( (context) => { // context包含SSR的request对象 return new NextSSRApolloClient({ cache: new NextSSRInMemoryCache(), link: getApolloClientLink(context?.req), // 传入req }); }, );
额外检查点
- 确认
ACCESS_TOKEN_COOKIE_NAME常量与实际Cookie名称完全一致 - 客户端Cookie的
path设置为/,确保在GraphQL接口路径下可被读取 - 同域场景下
credentials: "same-origin"配置正确,无需额外CORS设置
内容的提问来源于stack exchange,提问作者Pierre Olivier Tran
相关产品推荐
相关产品推荐

