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

如何在Next.js中通过getServerSideProps或middleware安全跨页传数据?

Next.js 服务端全局传参并隐藏API密钥的最佳方案

根据你的需求,核心是在服务端完成数据请求(隐藏密钥)并将数据全局传递给所有页面,以下分两种路由系统给出具体实现:

一、Pages Router(Next.js 12及以下,或13+兼容模式)

1. 利用 _app.tsx 的 getInitialProps

这是Pages Router里全局获取服务端数据的最直接方式,_app 是所有页面的入口,它的getInitialProps会在服务端执行,拿到的数据可以通过props传递给所有页面。

// pages/_app.tsx
import type { AppProps } from 'next/app';
import { createContext, useContext } from 'react';

// 创建全局Context
const GlobalDataContext = createContext(null);

function MyApp({ Component, pageProps, globalData }: AppProps & { globalData: any }) {
  return (
    <GlobalDataContext.Provider value={globalData}>
      <Component {...pageProps} />
    </GlobalDataContext.Provider>
  );
}

MyApp.getInitialProps = async (appContext) => {
  // 服务端执行,直接使用环境变量中的密钥,不会暴露给客户端
  const API_KEY = process.env.SECRET_API_KEY;
  const res = await fetch(`https://api.example.com/data?key=${API_KEY}`);
  const globalData = await res.json();

  // 保留页面自身的getInitialProps数据
  const appProps = await App.getInitialProps(appContext);
  return { ...appProps, globalData };
};

// 导出Context供子组件使用
export const useGlobalData = () => useContext(GlobalDataContext);
export default MyApp;

之后任意页面/组件可通过useGlobalData()直接读取全局数据。

2. 自定义API路由作为中间层

如果想解耦_app的请求逻辑,可以写一个服务端API路由,客户端页面调用自身API,密钥全程留在服务端:

// pages/api/get-global-data.ts
export default async function handler(req, res) {
  const API_KEY = process.env.SECRET_API_KEY;
  const apiRes = await fetch(`https://api.example.com/data?key=${API_KEY}`);
  const data = await apiRes.json();
  res.status(200).json(data);
}

然后在_app.tsx中请求该API并存入Context:

// pages/_app.tsx
import { useEffect, useState } from 'react';
import { createContext, useContext } from 'react';

const GlobalDataContext = createContext(null);

function MyApp({ Component, pageProps }) {
  const [globalData, setGlobalData] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      const res = await fetch('/api/get-global-data');
      const data = await res.json();
      setGlobalData(data);
    };
    fetchData();
  }, []);

  if (!globalData) return <div>加载中...</div>;

  return (
    <GlobalDataContext.Provider value={globalData}>
      <Component {...pageProps} />
    </GlobalDataContext.Provider>
  );
}

export const useGlobalData = () => useContext(GlobalDataContext);
export default MyApp;

二、App Router(Next.js 13+)

App Router里优先用Server Component实现,官方推荐且更高效:

1. 根Layout作为Server Component获取全局数据

根Layout默认是Server Component,可直接在服务端请求数据,再通过Context传递给所有客户端组件:

// app/layout.tsx
import { createContext } from 'react';

// 创建全局Context
export const GlobalDataContext = createContext(null);

// Server Component,无需加'use client'
export default async function RootLayout({ children }) {
  // 服务端执行,直接读取环境变量密钥
  const API_KEY = process.env.SECRET_API_KEY;
  const res = await fetch(`https://api.example.com/data?key=${API_KEY}`, { cache: 'no-store' });
  const globalData = await res.json();

  return (
    <html lang="en">
      <body>
        <GlobalDataContext.Provider value={globalData}>
          {children}
        </GlobalDataContext.Provider>
      </body>
    </html>
  );
}

客户端组件中读取Context:

// app/components/DataDisplay.tsx
'use client';

import { useContext } from 'react';
import { GlobalDataContext } from '../layout';

export default function DataDisplay() {
  const globalData = useContext(GlobalDataContext);
  return <div>全局数据:{globalData?.content}</div>;
}

2. 中间件结合请求头传递(动态场景)

如果需要根据请求地域、用户信息动态获取数据,可通过Middleware把数据放到响应头,再在根Layout中读取:

// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export async function middleware(request: NextRequest) {
  const API_KEY = process.env.SECRET_API_KEY;
  // 根据请求地域动态获取数据
  const region = request.geo?.region || 'default';
  const res = await fetch(`https://api.example.com/data?key=${API_KEY}&region=${region}`);
  const globalData = await res.json();

  // 将数据转为字符串存入自定义响应头(注意头大小限制,避免过大数据)
  const response = NextResponse.next();
  response.headers.set('x-global-data', JSON.stringify(globalData));
  return response;
}

根Layout中读取响应头:

// app/layout.tsx
import { headers } from 'next/headers';
import { createContext } from 'react';

export const GlobalDataContext = createContext(null);

export default async function RootLayout({ children }) {
  const headersList = headers();
  const globalDataStr = headersList.get('x-global-data');
  const globalData = globalDataStr ? JSON.parse(globalDataStr) : null;

  return (
    <html lang="en">
      <body>
        <GlobalDataContext.Provider value={globalData}>
          {children}
        </GlobalDataContext.Provider>
      </body>
    </html>
  );
}

关键注意事项

  • 所有涉及API密钥的请求必须在服务端环境执行(Server Component、API路由、Middleware、getInitialProps等),环境变量不要加NEXT_PUBLIC_前缀,否则会暴露给客户端。
  • 用Middleware传递数据时,注意浏览器对响应头的大小限制(一般为几KB),不要存放过大的数据集。
  • App Router优先使用Server Component方案,性能优于客户端请求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 15:54:55