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

Next.js API调用失败时如何展示自定义错误页面

问题现象
  • 已完成Next.js自定义404错误页开发,预期API调用失败时触发展示,但实际API返回404时,页面与路由匹配成功时的渲染结构完全一致,仅缺失API返回的业务数据
  • 复现规则:路由companies/neimans可正常拉取API数据渲染企业内容,输入错误路径companies/neiman时,控制台可观测到404状态报错,但既不展示自定义404页,也不展示Next.js默认404页,仅渲染结构一致的空数据页面
  • 项目结构:pages目录下包含companies文件夹,内有动态路由文件[companydata].tsx(负责拉取API数据动态渲染企业信息)、自定义错误页404.tsx
现有实现代码

API请求方法

export const getCompanies = async (routeData: string): Promise<Company> => {
  const client = getApiClient();
  const response = await client.get<Company>(`api/companies/${routeData}`);
  if (response) {
    return response.data;
  }
  return {} as Company;
}

已尝试的失效方案

曾在[companydata].tsx中判断返回的企业数据对象是否为空,为空则重定向到404页面,代码如下:

if (Object.keys(company).length === 0) {
  return <Redirect to="/company/404"/>;
}

方案问题:页面会始终展示404内容。通过日志打印发现页面会触发多次渲染,前6次渲染时数据为空对象,直到第6次渲染后接口数据才正式返回,导致404误触发,且未定位到多次提前渲染的原因。

数据请求封装

将getCompanies方法封装在getData函数中做统一并行调用,代码如下:

export const getData = async (companyName: string): Promise<[Company, Sales]> => {
  if (companyName) {
    return (await Promise.all([getCompanies(companyName), getSales()])) as [
      Company,
      Sales
    ];
  }
  return [{} as Company, {} as Sales];
};

页面组件逻辑

getData方法在[companydata].tsx的useEffect钩子中调用,对应代码如下:

const Company: NextPage = (): JSX.Element => {
  const [selectedCompany, setSelectedCompany] = useState<Company>({} as Company);
  const [salesAvailable, setSalesAvailable] = useState<boolean>(false);
  const [sales, setSales] = useState<Sales>({} as Sales);
  const router = useRouter();
  const {companydata} = router.query;

  useEffect(() => {
    const init = async (companyName: string) => {
      const [companyData, salesData] = await getData(companyName);

      if (companyData) {
        setSelectedCompany(companyData);
      }
      if (salesData) {
        setSalesAvailable(true);
        setSales(salesData);
      } else {
        setSalesAvailable(false);
      }
    }
    init(companydata as string);
  }, [companydata]);

  // 此处返回企业页面渲染内容
}
问题根因
  • Next.js动态路由[companydata].tsx会匹配所有companies/*格式的路径,无论路径参数对应的企业是否存在,路由本身都会匹配成功,因此Next.js不会自动触发404页面
  • 客户端侧useEffect在组件首次挂载完成后才会执行,组件首次渲染时router.query在水合阶段未准备完成、接口请求也未返回,初始状态的selectedCompany为空对象,这就是多次提前渲染空数据的核心原因
  • 现有getCompanies方法未捕获接口错误:API返回404时请求客户端会直接抛出异常,方法未做错误捕获、也未识别404状态,仅默认返回空对象,没有区分「请求中」「请求成功有数据」「请求失败404」三种状态
  • 之前的空对象判断逻辑未加加载态,在请求还未返回的加载阶段就判定为空跳转404,必然出现误触发。额外问题:通过重定向跳转到单独404路由会返回302状态码,不符合404语义,也不利于SEO。
修复方案
  1. 新增状态标识,区分请求中、请求成功、请求失败三种状态,避免加载阶段误判
// 组件内新增两个状态
const [loading, setLoading] = useState<boolean>(true);
const [isNotFound, setIsNotFound] = useState<boolean>(false);
  1. 改造API请求方法,捕获404错误,不要无脑返回空对象
export const getCompanies = async (routeData: string): Promise<Company | null> => {
  const client = getApiClient();
  try {
    const response = await client.get<Company>(`api/companies/${routeData}`);
    return response.data;
  } catch (err: any) {
    // 识别404状态返回null,其余错误可按需做全局异常处理
    if (err.response?.status === 404) {
      return null;
    }
    throw err;
  }
}
  1. 改造页面逻辑,等路由参数准备完成再发请求,请求过程中维持加载态,根据接口返回结果判断是否渲染404
const Company: NextPage = (): JSX.Element => {
  const [selectedCompany, setSelectedCompany] = useState<Company | null>(null);
  const [salesAvailable, setSalesAvailable] = useState<boolean>(false);
  const [sales, setSales] = useState<Sales>({} as Sales);
  const [loading, setLoading] = useState<boolean>(true);
  const [isNotFound, setIsNotFound] = useState<boolean>(false);
  const router = useRouter();
  const {companydata} = router.query;

  useEffect(() => {
    // 路由参数未就绪时不触发请求
    if (!companydata) return;
    const init = async (companyName: string) => {
      try {
        setLoading(true);
        const [companyData, salesData] = await getData(companyName);
        // 接口返回null代表企业不存在,标记404状态
        if (!companyData) {
          setIsNotFound(true);
          return;
        }
        setSelectedCompany(companyData);
        setSalesAvailable(!!salesData);
        if (salesData) setSales(salesData);
      } finally {
        setLoading(false);
      }
    }
    init(companydata as string);
  }, [companydata]);

  // 加载阶段渲染加载占位,不做数据判空
  if (loading) return <div>加载中...</div>
  // 识别到404时直接返回自定义404组件,无需路由重定向
  if (isNotFound) return <NotFound />;

  // 正常渲染企业页面内容
  return <div>{/* 企业信息展示逻辑 */}</div>
}
  1. SEO优化方案:如果需要返回正确的404状态码,可将数据请求迁移到getServerSideProps中,在服务端识别404后返回notFound: true,Next.js会自动渲染自定义404页面,同时返回标准404状态码
export async function getServerSideProps(context) {
  const { companydata } = context.params;
  try {
    const companyData = await getCompanies(companydata as string);
    if (!companyData) {
      return { notFound: true }
    }
    // 其余数据拉取逻辑
    return {
      props: { companyData /* 其余透传页面数据 */ }
    }
  } catch (err) {
    return { notFound: true }
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 21:00:56