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

Next.js 13+Urql 3静态生成中Guest Token获取问题求助

问题解答

1. 改用Cookie存储Token能否解决?

能。核心原因:

  • sessionStorage 是浏览器专属的本地存储,服务端(包括getStaticProps的执行环境)完全无法访问浏览器的存储对象,所以服务端执行查询时拿不到Token。
  • Cookie会随每次HTTP请求自动携带到服务端,Next.js中可以通过服务端API(比如cookies())直接读取Cookie里的Token,之后就能在Urql请求中带上这个Token完成鉴权。

2. 是否是Urql配置问题?

大概率是。Urql的客户端初始化需要区分服务端环境和客户端环境:

  • 客户端可以直接从Cookie/sessionStorage取Token,但服务端必须从请求携带的Cookie中获取Token,再注入到Urql的请求头里。如果你的Urql配置没做这种差异化处理,服务端发起的请求就不会带上Token,自然查询失败。

修正后的代码示例

1. 迁移Token存储到Cookie(客户端设置)

在客户端获取到Guest Token后,替换原sessionStorage存储逻辑为Cookie:

// 客户端代码,比如获取到Guest Token后
import { setCookie } from 'cookies-next';

const guestToken = '获取到的访客Token';
// 设置Cookie,确保服务端能读取到
setCookie('guest-token', guestToken, {
  path: '/',
  maxAge: 86400, // 有效期1天
  httpOnly: false, // 若需要客户端也能读取则设为false,仅服务端使用可设为true
  secure: process.env.NODE_ENV === 'production', // 生产环境开启HTTPS专属
});

2. 差异化配置Urql客户端

创建适配服务端/客户端的Urql客户端工厂函数:

// lib/urql-client.ts
import { createClient, dedupExchange, fetchExchange, Exchange } from 'urql';
import { cookies } from 'next/headers';

// 服务端专属鉴权Exchange:从Cookie取Token并添加到请求头
const serverAuthExchange: Exchange = ({ forward }) => async (ops$) => {
  const cookieStore = cookies();
  const guestToken = cookieStore.get('guest-token')?.value;

  return forward(
    ops$.map((op) => {
      if (!guestToken) return op;
      return {
        ...op,
        context: {
          ...op.context,
          fetchOptions: {
            ...op.context.fetchOptions,
            headers: {
              ...op.context.fetchOptions?.headers,
              // 替换为你的API要求的Token头字段,比如X-Guest-Token
              'X-Guest-Token': guestToken,
            },
          },
        },
      };
    })
  );
};

// 客户端专属鉴权Exchange:从Cookie取Token
const clientAuthExchange: Exchange = ({ forward }) => async (ops$) => {
  // 从Cookie读取Token
  const guestToken = document.cookie
    .split('; ')
    .find(row => row.startsWith('guest-token='))
    ?.split('=')[1];

  return forward(
    ops$.map((op) => {
      if (!guestToken) return op;
      return {
        ...op,
        context: {
          ...op.context,
          fetchOptions: {
            ...op.context.fetchOptions,
            headers: {
              ...op.context.fetchOptions?.headers,
              'X-Guest-Token': guestToken,
            },
          },
        },
      };
    })
  );
};

export const createUrqlClient = (isServer: boolean) => {
  const exchanges = [dedupExchange];

  // 根据环境添加对应的鉴权Exchange
  isServer ? exchanges.push(serverAuthExchange) : exchanges.push(clientAuthExchange);
  exchanges.push(fetchExchange);

  return createClient({
    url: 'https://你的API地址/graphql',
    exchanges,
  });
};

3. 在getStaticProps中使用服务端Urql客户端

// pages/目标页面.tsx(Next.js13 Pages Router)
import { createUrqlClient } from '../lib/urql-client';
import { gql } from 'urql';

// 你的GraphQL查询语句
const GET_CONTENT_QUERY = gql`
  query GetTargetContent {
    contentList {
      id
      title
    }
  }
`;

export async function getStaticProps() {
  // 创建服务端专属Urql客户端
  const client = createUrqlClient(true);
  const { data, error } = await client.query(GET_CONTENT_QUERY, {}).toPromise();

  if (error) {
    return { notFound: true };
  }

  return {
    props: { data },
    revalidate: 60, // 可选:开启增量静态再生
  };
}

export default function TargetPage({ data }) {
  return (
    <div>
      {/* 渲染服务端获取到的数据 */}
      {data?.contentList?.map(item => <div key={item.id}>{item.title}</div>)}
    </div>
  );
}

额外注意事项

  • 若使用Next.js13 App Router(app目录),getStaticProps需替换为generateStaticParams + 服务端组件逻辑,可配合@urql/next包简化服务端客户端的适配。
  • Cookie的path需设置为/,否则服务端可能无法读取到指定路径下的Cookie。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 15:35:39